mailman (423 pts) - 31 solves by Eth007
I’m sure that my post office is 100% secure! It uses some of the latest software, unlike some of the other post offices out there…
Flag is in ./flag.txt.
nc mailman.chal.imaginaryctf.org 1337
- Trivial heap and libc leak
- tcache poisoning to hiijack stdout
- FSOP on stdout to leak environ
- tcache poisoning on the fgets’s stackframe
- ROPchain that takes care of the seccomp
First let’s take at the version of the libc and at the protections inabled onto the binary.
$ checksec --file vuln
Full prot for the binary and classic partial RELRO for the already up-to-date libc. The binary loads a seccomp that allows only the read, write, open, fstat and exit system calls.
By reading the code in IDA the main looks like this:
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
The program allows to create a chunk of any size, filling it with user-supplied input with fgets. We can print its content or free it. The bug lies in the free handler that doesn’t check if a chunk has already been free’d.
Before bypassing the seccomp we need to get code execution, to do so I will use the very classic exploitation flow:
FSOP stdout to leak environ =>
ROPchain. I could have used an angry FSOP to directly get code execution by hijjacking the vtable used by the wide operations in stdout, given actually it is not checked against a specific address range as it is the case for the
_vtable. To get code execution, we need to get the heap and libc base addresses.
To get a heap leak we can simply do defeat safe-linking:
To get an arbitrary read / write I used the house of botcake technique. I already talked about it more deeply there. During this house I put a chunk in the unsortedbin, leaking the libc:
add(0, 0x100, b"YY")
The house of botcake is very easy to understand, it is useful when you can trigger some double free bug. It is basically:
- Allocate 7 0x100 sized chunks to then fill the tcache (7 entries).
- Allocate two more 0x100 sized chunks (prev and a in the example).
- Allocate a small “barrier” 0x10 sized chunk.
- Fill the tcache by freeing the first 7 chunks.
- free(a), thus a falls into the unsortedbin.
- free(prev), thus prev is consolidated with a to create a large 0x221 sized chunk that is remains in the unsortedbin.
- Request one more 0x100 sized chunk to let a single entry available in the tcache.
- free(a) again, given a is part of the large 0x221 sized chunk it leads to an UAF. Thus a falls into the tcache.
- That’s finished, to get a write what where we just need to request a 0x130 sized chunk. Thus we can hiijack the next fp of a that is currently referenced by the tcache by the location we wanna write to. And next time two 0x100 sized chunks are requested, the second one will be the target location.
for i in range(7):
Then, at the next
stdout will be returned! Something important to notice if you’re a beginner in heap exploitation is how the safe-linking is handled, you have to xor the target location with
((chunk_location) >> 12)). Sometimes the result is not properly aligned leading to a crash, to avoid this you can add or sub 0x8 to your target location.
To leak the address of the stack we can use a FSOP on stdout. To understand how a such attack does work I advice you to read my this write-up. The goal is to read the stack address stored at
libc.sym.environ within the libc. Which gives:
# tcache => stdout
Now we leaked everything we just need to reuse the arbitrary write provided thanks to the house of botcake, given we already have overlapping chunks, to get another arbitrary write we just need to put the large chunk in a large tcache and the overlapped chunk in the
0x100 tcache, then we just have to corrupt
victim->fp to the saved rip of the
fgets stackframe :). It gives:
rop = pwn.ROP(libc, base=stack)
$ python3 exploit.py REMOTE HOST=mailman.chal.imaginaryctf.org PORT=1337