Here is another very interesting challenge from Rootme. The title says ELF – no software breakpoints.
Let’s run the file command and see.
% file ch20.bin ch20.bin: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, stripped
The executable seemed to be striped.
Next I examined the sections in file and the .text section starts at 0x08048080
This is the disassembly of the text section. I spent some time trying to understand the logic. Well let’s see what this is 🙂
ch20.bin: file format elf32-i386Disassembly of section .text:.text>:b8 04 00 00 00 mov eax,0x4bb 01 00 00 00 mov ebx,0x1a: b9 a1 91 04 08 mov ecx,0x80491a1f: ba 26 00 00 00 mov edx,0x26cd 80 int 0x80b8 03 00 00 00 mov eax,0x3b: db xor ebx,ebxd: b9 88 91 04 08 mov ecx,0x8049188a2: ba 33 00 00 00 mov edx,0x33a7: cd 80 int 0x80a9: c9 xor ecx,ecxab: b8 80 80 04 08 mov eax,0x8048080b0: bb 23 81 04 08 mov ebx,0x8048123b5: e8 5b 00 00 00 call 0x8048115ba: ca mov edx,ecxbc: b9 19 00 00 00 mov ecx,0x19c1: b8 55 91 04 08 mov eax,0x8049155c6: bb 88 91 04 08 mov ebx,0x8049188cb: d1 ca ror edx,1cd: a 44 08 ff mov al,BYTE PTR [eax+ecx*1-0x1]d1: a 5c 0b ff mov bl,BYTE PTR [ebx+ecx*1-0x1]d5: d8 xor al,bld7: d0 xor al,dld9: b jne 0x80480f6db: dec ecxdc: e3 jne 0x80480c1de: b8 04 00 00 00 mov eax,0x4e3: bb 01 00 00 00 mov ebx,0x1e8: b9 24 91 04 08 mov ecx,0x8049124ed: ba 26 00 00 00 mov edx,0x26f2: cd 80 int 0x80f4: eb 16 jmp 0x804810cf6: b8 04 00 00 00 mov eax,0x4fb: bb 01 00 00 00 mov ebx,0x1b9 4a 91 04 08 mov ecx,0x804914aba 0b 00 00 00 mov edx,0xba: cd 80 int 0x80c: b8 01 00 00 00 mov eax,0x1db xor ebx,ebxcd 80 int 0x80c3 sub ebx,eaxc9 xor ecx,ecxadd cl,BYTE PTR [eax]b: c1 c1 03 rol ecx,0x3e: inc eaxf: b dec ebxf7 jne 0x8048119c3 ret
Let’s open in gdb and start debugging.
0x8048080: mov eax,0x4 0x8048085: mov ebx,0x1 0x804808a: mov ecx,0x80491a1 0x804808f: mov edx,0x26 0x8048094: int 0x80
It will first print the text “Welcome to Root-Me Challenges\r\nPass: ” using the write system call.
0x8048096: mov eax,0x3 0x804809b: xor ebx,ebx 0x804809d: mov ecx,0x8049188 0x80480a2: mov edx,0x33 0x80480a7: int 0x80
Next it will store the input in 0x8049188 using the read system call and store in ecx.
0x80480a9: xor ecx,ecx 0x80480ab: mov eax,0x8048080 0x80480b0: mov ebx,0x8048123 0x80480b5: call 0x8048115
Next it will store 0x8048080 which means the starting address of the text section in eax and the ending section 0x8048123 of the text section in ebx. Next it calls 0x8048115
0x8048115: sub ebx,eax 0x8048117: xor ecx,ecx 0x8048119: add cl,BYTE PTR [eax] 0x804811b: rol ecx,0x3 0x804811e: inc eax 0x804811f: dec ebx 0x8048120: jne 0x8048119 0x8048122: ret
Next it subtracts ebx by eax which results in the size of the text section which is 163. Zero outs the ecx register. Add each opcode of the program’s text section into the cl register. Rotate left ecx by 3 bits and store in ecx. Next increment to the next opcode in eax and decrement the counter. If ebx not equal to zero keep looping. As you can clearly see this is a loop which will rotate left each opcode of the text section and sum in the cl register. This seems to be calculating a checksum for the serial routine.
This is known anti debugging technique in which can’t place any software breakpoints in the application itself. Because when we hit a breakpoint in a user mode applications a 0xCC (INT 3) will be placed in the code, hence the calculation will be wrong and will generate a wrong checksum. So, while debugging even we enter the correct password we won’t be validated because the checksum will be wrong.
The problem arises in calculating the checksum. What I did was I extracted the opcodes of the text section like we extract shellcode 😉 Then we can calculate the checksum like in the program.
extern printfextern exitglobal mainsection .textmain:lea esi, [shellcode] ; load offset of shellcodemov ebx, shellcode_len ; mov the len of shellcodexor ecx, ecx ; Zero out ecx_loop:add cl, [esi] ; add opcode to clrol ecx, 0x3 ; Rotate left ecx by 3inc esi ; incremenet esidec ebx ; decrement ebxjnz _loop ; if ebx != 0 looppush ecx ; push the result to stackpush fmt_checksum ; push the format stringcall printf ; print itmov ebx, [esp+4] ; mov the result from stack to ebxadd esp, 0x8 ; Clear the stackpush 1call exitsection .datafmt: db "Checksum: %x",0xa,0shellcode:db 0xb8,0x04,0x00,0x00,0x00,0xbb,0x01,0x00,0x00,0x00db 0xb9,0xa1,0x91,0x04,0x08,0xba,0x26,0x00,0x00,0x00db 0xcd,0x80,0xb8,0x03,0x00,0x00,0x00,0x31,0xdb,0xb9db 0x88,0x91,0x04,0x08,0xba,0x33,0x00,0x00,0x00,0xcddb 0x80,0x31,0xc9,0xb8,0x80,0x80,0x04,0x08,0xbb,0x23db 0x81,0x04,0x08,0xe8,0x5b,0x00,0x00,0x00,0x89,0xcadb 0xb9,0x19,0x00,0x00,0x00,0xb8,0x55,0x91,0x04,0x08db 0xbb,0x88,0x91,0x04,0x08,0xd1,0xca,0x8a,0x44,0x08db 0xff,0x8a,0x5c,0x0b,0xff,0x30,0xd8,0x30,0xd0,0x75db 0x1b,0x49,0x75,0xe3,0xb8,0x04,0x00,0x00,0x00,0xbbdb 0x01,0x00,0x00,0x00,0xb9,0x24,0x91,0x04,0x08,0xbadb 0x26,0x00,0x00,0x00,0xcd,0x80,0xeb,0x16,0xb8,0x04db 0x00,0x00,0x00,0xbb,0x01,0x00,0x00,0x00,0xb9,0x4adb 0x91,0x04,0x08,0xba,0x0b,0x00,0x00,0x00,0xcd,0x80db 0xb8,0x01,0x00,0x00,0x00,0x31,0xdb,0xcd,0x80,0x29db 0xc3,0x31,0xc9,0x02,0x08,0xc1,0xc1,0x03,0x40,0x4bdb 0x75,0xf7,0xc3shellcode_len equ $-shellcode
The checksum is “ac77e166”
Let’s proceed and see knowing we will get a wrong checksum while inside the debugger.
0x80480ba: mov edx,ecx 0x80480bc: mov ecx,0x19 0x80480c1: mov eax,0x8049155 0x80480c6: mov ebx,0x8049188 0x80480cb: ror edx,1 0x80480cd: mov al,BYTE PTR [eax+ecx*1-0x1] 0x80480d1: mov bl,BYTE PTR [ebx+ecx*1-0x1] 0x80480d5: xor al,bl 0x80480d7: xor al,dl 0x80480d9: jne 0x80480f6 0x80480db: dec ecx 0x80480dc: jne 0x80480c1 0x80480de: mov eax,0x4 0x80480e3: mov ebx,0x1 0x80480e8: mov ecx,0x8049124 0x80480ed: mov edx,0x26 0x80480f2: int 0x80
Next we have the serial routine which is a loop that. The checksum will be moved to edx and ecx will contain 25 which is the length of our password. We see that eax contains 0x8049155 which is 25 bytes of the key.
The user entered password will be stored in ebx. The checksum edx will be rotated to right by 1 and stored in edx. Mov al, [eax+ecx*1-0x1] means the starting address of the key bytes + 25 which will point to the 25th byte of the key 0xe0. Likewise this will place the each byte starting from the last byte in descending order basically and place in the al register.
Every last byte of our entered password will be store in the bl register like the above. Next the key byte will XORed by the entered password byte. Next the result will be XORed by the checksum byte. If the result is not equal to zero we will jump to bad boy which is 0x80480f6. If not we keep on looping. If loop went successfully it will print the message 0x8049124 “Well done man, use this pass to flag!\n”
The program basically check like this:
Password ^ Key byte ^ Checksum == 0
To get the password basically we just have to XOR the checksum with the key byte.
Checksum ^ Key byte = Password
After looking at the application logic since we calculated the checksum before it was easy to apply the serial routine and get the password like in the program, Simple XOR logic 😉
I wrote the keygen using NASM. It’s self-explanatory.
Well, here’s the high level version of the keygen written in C. I have written in a way that it would compile under both the GNU C compiler and the Microsoft’s Visual C compiler. This is due to the way both compilers handle inline assembly in two different formats.
I hope this challenge was fun and interesting 🙂