SVATTT 2019 Quals 0day Hunter

On 3rd November 2019, me and my team joined the SVATTT (Asean Student Contest on Information Security) qualification round. We joined under the name “noobiens” and luckily ranked second in the South region. This is our writeup to the reversing challenge “0day hunter”. This challenge is from @anhdaden, who recently claimed a VMWare bug to escape virtual box. Without furthur ado, let’s get started.

The challenge

The challenge gives us two folders, challenge and tool. Inside tool, there is an AFL binary. AFL is a fuzzing tool created by Google, you can find the source for the tool on Github. However, this challenge uses a modified version of the fuzzer. We took quite a time looking on AFL for the solution but it was not the right approach. The challenge folder has a binary named fileinfo.exe, fuzz.bat, a folder seeds and a winafl.dll dynamic library file.

We are told to find the flag in fileinfo.exe and so we did, the file is not very big, it reads the input file and displayes information of the file. The below python pseudocode is the program’s mechanics.

if __name__ == "__main__":
  import sys
  if len(sys.argv) != 2:
  filename = sys.argv[1]
  filebuf = open(filename, 'rb').read()

The first 4 functions is very small and understandable by their name, but the printFileType function is huge. However, looking through the first few instructions, it basically checks the first few bytes for signature. If we went to the strings section, we can see strings like:

.rdata:0000000000405050	00000020	C	Palm Desktop To Do Archive file
.rdata:0000000000405070	00000023	C	Palm Desktop Calendar Archive file
.rdata:0000000000405093	0000001B	C	Computer icon encoded file
.rdata:00000000004050AE	00000019	C	MPEG Program Stream file
.rdata:00000000004050C7	00000018	C	WebAssembly binary file
.rdata:00000000004050E0	0000001F	C	PCAP Next Generation Dump file
.rdata:00000000004050FF	00000014	C	PostScript document
.rdata:0000000000405113	0000000D	C	PDF document

And if we look further, we’ll see this magic string

.rdata:00000000004052BE	0000000C	C	SVATTT file

And this points us toward

.text:000000000040204E                 cmp     eax, 'S'
.text:0000000000402051                 jz      loc_40258B
.text:0000000000402057                 jmp     loc_40273A

.text:000000000040258B loc_40258B:                             ; CODE XREF: sub_401FC7+8A↑j
.text:000000000040258B                 mov     rax, [rbp+filebuf]
.text:000000000040258F                 add     rax, 1
.text:0000000000402593                 movzx   eax, byte ptr [rax]
.text:0000000000402596                 cmp     al, 'V'
.text:0000000000402598                 jnz     short loc_4025EB
.text:000000000040259A                 mov     rax, [rbp+filebuf]
.text:000000000040259E                 add     rax, 2
.text:00000000004025A2                 movzx   eax, byte ptr [rax]
.text:00000000004025A5                 cmp     al, 'A'
.text:00000000004025A7                 jnz     short loc_4025EB
.text:00000000004025A9                 mov     rax, [rbp+filebuf]
.text:00000000004025AD                 add     rax, 3
.text:00000000004025B1                 movzx   eax, byte ptr [rax]
.text:00000000004025B4                 cmp     al, 'T'
.text:00000000004025B6                 jnz     short loc_4025EB
.text:00000000004025B8                 mov     rax, [rbp+filebuf]
.text:00000000004025BC                 add     rax, 4
.text:00000000004025C0                 movzx   eax, byte ptr [rax]
.text:00000000004025C3                 cmp     al, 'T'
.text:00000000004025C5                 jnz     short loc_4025EB
.text:00000000004025C7                 mov     rax, [rbp+filebuf]
.text:00000000004025CB                 add     rax, 5
.text:00000000004025CF                 movzx   eax, byte ptr [rax]
.text:00000000004025D2                 cmp     al, 'T'
.text:00000000004025D4                 jnz     short loc_4025EB
.text:00000000004025D6                 lea     rcx, aSvatttFile ; "SVATTT file"
.text:00000000004025DD                 call    puts
.text:00000000004025E2                 mov     rcx, [rbp+filebuf]
.text:00000000004025E6                 call    svattt_print

It checks the first 6 bytes for a matching “SVATTT”, and calls a function.

.text:0000000000401A11 sbox            = byte ptr -120h
.text:0000000000401A11 key             = byte ptr -20h
.text:0000000000401A11 output          = qword ptr -10h
.text:0000000000401A11 counter         = dword ptr -4
.text:0000000000401A11 filebuf         = qword ptr  10h
.text:0000000000401A11                 push    rbp
.text:0000000000401A12                 sub     rsp, 140h
.text:0000000000401A19                 lea     rbp, [rsp+80h]
.text:0000000000401A21                 mov     [rbp+0C0h+filebuf], rcx
.text:0000000000401A28                 mov     [rbp+0C0h+output], 0
.text:0000000000401A33                 mov     dword ptr [rbp+0C0h+key], 0
.text:0000000000401A3D                 mov     word ptr [rbp+0C0h+key+4], 0
.text:0000000000401A46                 mov     [rbp+0C0h+key+6], 0
.text:0000000000401A4D                 mov     [rbp+0C0h+key], 9Fh
.text:0000000000401A54                 mov     [rbp+0C0h+key+4], 98h
.text:0000000000401A5B                 mov     [rbp+0C0h+key+1], 9Ah
.text:0000000000401A62                 mov     [rbp+0C0h+key+3], 98h
.text:0000000000401A69                 mov     [rbp+0C0h+key+2], 8Dh
.text:0000000000401A70                 mov     [rbp+0C0h+key+5], 98h
.text:0000000000401A77                 mov     [rbp+0C0h+counter], 0
.text:0000000000401A81                 jmp     short loc_401AAE
.text:0000000000401A83 ; ---------------------------------------------------------------------------
.text:0000000000401A83 loc_401A83:                             ; CODE XREF: svattt_print+A4↓j
.text:0000000000401A83                 mov     eax, [rbp+0C0h+counter]
.text:0000000000401A89                 cdqe
.text:0000000000401A8B                 movzx   eax, [rbp+rax+0C0h+key]
.text:0000000000401A93                 xor     eax, 0FFFFFFCCh
.text:0000000000401A96                 mov     edx, eax
.text:0000000000401A98                 mov     eax, [rbp+0C0h+counter]
.text:0000000000401A9E                 cdqe
.text:0000000000401AA0                 mov     [rbp+rax+0C0h+key], dl
.text:0000000000401AA7                 add     [rbp+0C0h+counter], 1
.text:0000000000401AAE loc_401AAE:                             ; CODE XREF: svattt_print+70↑j
.text:0000000000401AAE                 cmp     [rbp+0C0h+counter], 5
.text:0000000000401AB5                 jle     short loc_401A83
.text:0000000000401AB7                 mov     rcx, [rbp+0C0h+filebuf]
.text:0000000000401ABE                 call    strlen
.text:0000000000401AC3                 add     rax, 1
.text:0000000000401AC7                 mov     rcx, rax
.text:0000000000401ACA                 call    malloc
.text:0000000000401ACF                 mov     [rbp+0C0h+output], rax
.text:0000000000401AD6                 cmp     [rbp+0C0h+output], 0
.text:0000000000401ADE                 jz      short loc_401B5E
.text:0000000000401AE0                 lea     rdx, [rbp+0C0h+sbox]
.text:0000000000401AE4                 lea     rax, [rbp+0C0h+key]
.text:0000000000401AEB                 mov     rcx, rax
.text:0000000000401AEE                 call    sub_4015D8
.text:0000000000401AF3                 mov     rdx, [rbp+0C0h+output]
.text:0000000000401AFA                 lea     rax, [rbp+0C0h+sbox]
.text:0000000000401AFE                 mov     r8, rdx
.text:0000000000401B01                 mov     rdx, [rbp+0C0h+filebuf]
.text:0000000000401B08                 mov     rcx, rax
.text:0000000000401B0B                 call    sub_401977
.text:0000000000401B10                 mov     rax, [rbp+0C0h+output]
.text:0000000000401B17                 mov     r8d, 34h
.text:0000000000401B1D                 lea     rdx, blob       ; Size
.text:0000000000401B24                 mov     rcx, rax
.text:0000000000401B27                 call    memcmp
.text:0000000000401B2C                 test    eax, eax
.text:0000000000401B2E                 jnz     short loc_401B5E
.text:0000000000401B30                 mov     rdx, [rbp+0C0h+filebuf]
.text:0000000000401B37                 lea     rcx, aMessageS  ; "Message: %s\n"
.text:0000000000401B3E                 call    printf
.text:0000000000401B43                 mov     edx, '{'
.text:0000000000401B48                 mov     rcx, [rbp+0C0h+filebuf]
.text:0000000000401B4F                 call    strchr
.text:0000000000401B54                 test    rax, rax
.text:0000000000401B57                 jz      short loc_401B5E
.text:0000000000401B59                 call    abort
.text:0000000000401B5E ; ---------------------------------------------------------------------------
.text:0000000000401B5E loc_401B5E:                             ; CODE XREF: svattt_print+CD↑j
.text:0000000000401B5E                                         ; svattt_print+11D↑j ...
.text:0000000000401B5E                 nop
.text:0000000000401B5F                 add     rsp, 140h
.text:0000000000401B66                 pop     rbp
.text:0000000000401B67                 retn

Here is the python pseudocode of the above function:

def svatttfile(filebuf):
  key = [0x9f, 0x9a, 0x8d, 0x98, 0x98, 0x98]
  for i in range(len(key)):
    key[i] ^= 0xcc
  # guess what it will output? [S, V, A, T, T, T]

  arr1 = [0 for i in range(0x60)]    # rbp-0x60
  arr2 = [0 for i in range(size(filebuf))]

  secret_func1(key, arr1)
  secret_func2(arr1, filebuf, arr2)

  if (memcmp(arr2, SOMEARR, 34) == 0):
    print("Message %s" % filebuf)
    if ('{' in filebuf):

At this point, our team thought that we needed to fuzz to get input of the secret_func1 and secret_func2. We spent a lot of time looking for ways to generate the input file without knowing that it is not intended. Then the hint came:

It’s RC4

The author wanted us to reverse the functions, not fuzzing it. We get back to the file, throwing all the RC4 implementation online we could find to solve this challenge. We had the result buffer SOMEARR, and the key, we just need to decrypt SOMEARR using RC4. But it didn’t work, secret_func1 looks just like KSA step in RC4, but PRGA is nowhere alike. Out teamate @pickaxe found the differences, and rewrote the script. And we get message.

def crypt():
	plain = "DACB317F819820386CCF03A7FF04645E46FD5FE7037EB1DABBE1EB0E67703BCCF29E049381284107F1F9079ACF36DE42970C25A7".decode('hex')
	S = [0xD7,0x0E,0xF6,0x9E,0xC4,0x9C,0x7C,0xB2,0x8D,0x44,0x3B,0x1A,0x20,0xD6,0x17,0xCE,0x74,0x52,0xFF,0x35,0xB5,0x58,0xB8,0xF7,0xF8,0x4C,0x95,0xE6,0xAC,0x70,0xE7,0x86,0x43,0x62,0xE8,0x42,0xC3,0x89,0x59,0xE9,0x93,0xE4,0xEC,0xE2,0xEA,0xF9,0x2F,0x47,0xE0,0x05,0x73,0xED,0x4A,0xEB,0xA4,0x5A,0xCD,0xB1,0x46,0x80,0x02,0xEE,0xA9,0x3A,0x15,0xB4,0xB7,0xFA,0x03,0xC2,0x04,0xDF,0xEF,0xBE,0xDE,0x6F,0x5B,0xD3,0x9B,0x79,0x83,0xDD,0x25,0xF0,0xAB,0xA6,0x92,0x65,0xDC,0x38,0xFB,0x32,0xD2,0xF2,0x87,0x0F,0xDB,0x91,0xF1,0x2A,0x5D,0x21,0xCB,0x96,0x99,0xDA,0x10,0x76,0xF3,0x1D,0x7B,0xD1,0x18,0xFC,0xB3,0x28,0x81,0x1E,0x55,0xA8,0x51,0x01,0xC1,0x36,0xF4,0x26,0x71,0xCA,0x24,0xC5,0xD0,0x56,0x63,0x90,0xD9,0xB9,0x7D,0xA3,0x69,0x8C,0xFD,0xF5,0x6A,0xD8,0x1B,0x0D,0x45,0xFE,0x72,0x66,0xCF,0x77,0x09,0xC9,0x7E,0xC0,0x6C,0x78,0x0C,0x4F,0x2C,0x1C,0x7F,0x00,0x8E,0x29,0x97,0x13,0x9A,0x39,0x14,0x3D,0x07,0x54,0x88,0x98,0x57,0x16,0x0A,0x27,0x5F,0x84,0xA1,0x11,0xA0,0x41,0x9D,0x37,0x85,0xB6,0x4B,0x6E,0x2E,0x4E,0x68,0xBF,0x64,0xAD,0x12,0x34,0xA7,0x0B,0x3F,0x8A,0x5C,0x67,0x8B,0xBC,0x40,0xBD,0xBA,0xCC,0x33,0x06,0x50,0x31,0x82,0x61,0x94,0x30,0x60,0x08,0xAE,0xC7,0x3E,0x1F,0x8F,0xD4,0x19,0xA2,0xD5,0x48,0x3C,0x23,0xE5,0xBB,0xE3,0x5E,0x75,0x49,0x6D,0xC6,0x6B,0xB0,0x7A,0xAA,0x4D,0xAF,0xC8,0xE1,0x22,0x9F,0x53,0x2D,0xA5,0x2B]

	cipherList = []
	i = 0
	j = 0

	for m in range(len(plain)):
		i = (i + 1) % 256
		j = (j + S[i]) % 256
		S[i], S[j] = S[j], S[i]
		k = S[(S[i] + S[j]) % 256]
		cipherList.append(k ^ ord(plain[m]))

	return cipherList

print(''.join(map(chr, crypt())))

where plain is SOMEARR, and S is the arr1 we get after secret_func1.

SVATTT hint: you may wanna take a look at winafl.dll

Thought we are finished, but nope. We went on looking at winafl.dll. There are many functions, I cannot find any function that looks like a flag function. Turns out, there is a function that will modify the check buffer while the fuzzer is running (probably how fuzzer works). Again, my teammate @pickaxe found it. He noticed a function that changes the memcmp to another function.

mov     rcx, [rbx]
lea     rdx, aMemcmp    ; "memcmp"
call    dr_get_proc_address
lea     rdx, foo
xor     r8d, r8d
mov     rcx, rax
call    drwrap_wrap

(I saw this function once but a whole lot numbers didn’t get me anything so I skipped it, didn’t know I was looking at the right place)

Again, it’s RC4, the buffer compared to SOMEARR is changed into to

mov     dword ptr [rbp+var_40], 7F31CBDAh
mov     dword ptr [rbp+var_40+4], 387B9881h
mov     dword ptr [rbp+var_38], 0A707D571h
mov     dword ptr [rbp+var_38+4], 526F52F0h
mov     dword ptr [rbp+var_30], 0F153F108h
mov     dword ptr [rbp+var_30+4], 9ABF6051h
mov     [rbp+var_28], 55ACF2BAh
mov     [rbp+var_24], 9F3D7462h
mov     [rbp+var_20], 931AD9BCh
mov     [rbp+var_1C], 393E339Bh
mov     [rbp+var_18], 8107ABE1h
mov     [rbp+var_14], 469627C0h
mov     [rbp+var_10], 0B62505CDh

Quickly I re-wrote the script to decrypt this

from pwn import *
plain = ''.join([

Rerun the file, we got:


When I submitted this flag, it was 5h42m. Only 3 minutes before the end of the contest. This challenge makes our team rise from rank 11 to rank 4 country. It was a big relief, I thought we couldn’t make it. Thank you @pickaxe for being very patient. That last minute was a huge achievement.

The flags shows a function in DynamoRIO API to wraps and replace a function, probably what the author used to wrap and replace memcmp.

Just Chicken

I like programming languages. I want to read those compiler stuff, but I’m just a noob.