Write me a book
Write me a Book
349Give back to the library! Share your thoughts and experiences!
The flag can be found in /flag
Elma
nc 34.124.157.94 12346
Write me a book is a heap challenge I did during the Grey Cat The Flag 2023 Qualifiers. You can find the tasks and the exploit here.
TL;DR
To manage to read the flag we have to:
- create overlapping chunks due to an oob write vulnerability in
rewrite_books
- tcache poisoning thanks to the overlapping chunks
- Overwrite the first entry of
@books
to then be able to rewrite 4 entries of@books
by setting a large size. - With the read / write primitives of
@books
we leak&stdout@glibc
andenviron
, this way getting a libc and stack leak. - This way we can simply ROP over a given stackframe.
General overview
Let’s take a look at the protections and the version of the libc:
1 | $ ./libc.so.6 |
So a very recent one with standards protections. Then let’s take a look at the binary:
1 | $ checksec --file chall |
The binary isn’t PIE based and does have a seccomp that allows only read
, write
, open
and exit
. Which will make the exploitation harder (but not that much).
Code review
The main
looks like this:
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
We have to give a signature (12 bytes max) sorted in author_signatures
, then the program is allocating a lot of chunks in secure_library
. Finally it calls write_books
which contains the main logic:
1 | unsigned __int64 write_books() |
There are basically three handlers:
1337
, we can leak only one time the address of a given allocated chunk.4
returns.3
free a chunk.1
add a book.2
edit a book.
Let’s take a quick look at each handler, first the free handler:
1 | unsigned __int64 throw_book() |
It only checks is the entry exists and if the index is in the right range. if it does it frees the entry and zeroes it.
Then, the add handler:
1 | unsigned __int64 write_book() |
We can allocate a chunk between 0x10
and 0x20 + 0x10
bytes and after we wrote in it the signature initially choose at the begin of the execution is put right after the end of the input.
Finally comes the handler where lies the vuln, the edit handler:
1 | unsigned __int64 rewrite_book() |
As you can read there is an out of bound write if we input books[idx].size
bytes, indeed given the chunk stores only books[idx].size
bytes the signature writes over the current chunk. And most of the time on the header (and especially the size) of the next chunk allocated in memory resulting an overlapping chunk.
Exploitation
Given we can get overlapping chunks we’re able to do tcache poisoning on the 0x40
tcachebin (to deeply understand why I advice you to read the exploit and to run it into gdb). At this point we can simply write the first entry of @books
that is stored at a fixed memory area within the binary (no PIE). In this new entry we could write a pointer to itself but with a large size in order to be able to write several entries of @books
. When it is done we could write these entries:
1 | edit(1, pwn.flat([ |
This way we can easily leak libc.
Leaking libc
Leaking libc is very easy given we already setup the entries of @books
. We can replace free@GOT
by puts@plt
. This way the next time free will be called on an entry, it will leak the datas towards which the entry points. Which means free(book[1])
leaks the address of stdout within the libc.
1 | STDOUT = 0x21a780 |
Leaking the stack
Leaking the libc is cool but given the binary has a seccomp we cannot write one_gadgets on __malloc_hook
or __free_hook
or within the GOT (of the libc or of the binary) because of the seccomp. We have to do a ROPchain, to do so we could use setcontext
but for this libc it is made around rdx
that we do not control. Or we could simply leak environ
to get the address of a stackframe from which we could return. That’s what we gonna do on the rewrite_books
stackframe.
1 | def leak_environ(idx): |
ROPchain
Everything is ready for the ROPchain, we cannot use mprotect to use a shellcode within the seccomp forbids it. We just have to set the first entry to the stackframe we’d like to hiijack and that’s it, then we just need call edit on this entry and the ROPchain is written and triggered at the return of the function!
1 | rop = pwn.ROP(libc, base=stackframe_rewrite) |
PROFIT
Finally:
1 | nasm@off:~/Documents/pwn/greycat/writemeabook/dist$ python3 exploit.py REMOTE HOST=34.124.157.94 PORT=12346 |
Conclusion
That was a nice medium heap challenge, even though that was pretty classic. You can find the tasks and the exploit here.
Annexes
Final exploit (with comments):
1 | #!/usr/bin/env python |