804845c: e4 83 in al,0x83
804845e: f0 55 lock push ebp
8048460: 57 push edi
8048461: e5 53 in eax,0x53
8048463: 81 56 89 0c 45 89 ec adc DWORD PTR [esi-0x77],0xec89450c
804846a: 00 01 add BYTE PTR [ecx],al
804846c: 8b 44 00 30 mov eax,DWORD PTR [eax+eax*1+0x30]
8048470: 00 00 add BYTE PTR [eax],al
8048472: 89 24 a1 mov DWORD PTR [ecx+eiz*4],esp
8048475: 65 00 84 14 1c c7 c0 add BYTE PTR gs:[esp+edx*1+0x44c0c71c],al
Obviously, we had to fix it. Since the name of challenge was miXer, we thought we probably had to xor it with some key to obtain the original code. We could guess correct the first bytes based on the opcodes of a typical function prologue:
55 push ebp
89 e5 mov ebp,esp
Assuming the key was four bytes long, we started to brute-force the last byte... but it didn't yield any valid results. Therefore, we took a closer look at the numeric byte values and realized that the opcodes were not really modified, only shuffled around using a specific pattern: the fourth byte should be the first one, the tenth should be second and the sixth should be third. Now, we had to move the remaining bytes around in the right way to end up with proper function code.We could make further progress by taking advantage of the fact that gcc tends to align stack to 16 bytes at the beginning of function execution with the following instruction:
83 e4 f0 and esp,0xfffffff0
At this point, we are left with four more bytes within the first 10-byte block: 53, 56, 57 and 81. The 81 byte is the first opcode of "sub esp, xxx" - a stack allocation, and the rest stand for "push ebx", "push esi" and "push edi", respectively. We could determine the order by investigating common function prologues, until we found the following one which matched:
55 push ebp
89 e5 mov ebp,esp
57 push edi
56 push esi
53 push ebx
83 e4 f0 and esp,0xfffffff0
81 ec 30 04 00 00 sub esp,0x430
If we assume that the entire function bytes are shuffled in 10-byte blocks, each in the exact same order as the first (recovered) one, we end up with the following solution in Python:
TEXT_START = 0x8048370
MAIN_BEG = 0x804845c - TEXT_START + 0x00370
MAIN_END = 0x8048830 - TEXT_START + 0x00370
with open('/tmp/miXer.elf.5f96dea48b8c8ab66898e902d3c98b82') as f:
data = f.read()
code = data[MAIN_BEG:MAIN_END]
data2= list(data)
flip = lambda x: (x[1],x[0])
ctable = dict(map(flip,enumerate(code[:10])))
ctable = [ ctable[c] for c in "\x55\x89\xe5\x57\x56\x53\x83\xe4\xf0\x81"]
for i in range(len(code)/10):
for j in range(10):
data2[MAIN_BEG+i*10+j] = code[i*10+ctable[j]]
with open('/tmp/mixfix.bin','w') as f: f.write(''.join(data2))
Let's see if it works:
[mak@localhost tmp]$ python2 x.py [mak@localhost tmp]$ chmod +x mixfix.bin [mak@localhost tmp]$ ./mixfix.bin ; echo y0ur.f1rst.fl4g [mak@localhost tmp]$
Done, +2000pts. :)
[0] http://shell-storm.org/repo/CTF/PHDays-Quals-2014/MiXer-2000pts/