This is the writeup for the pwn challenge called “La casa de papel” I made for the Hackon 2024 CTF. You can download everything necessary to deploy the challenge from here. Sadly the challenge got 0 solves.
TL;DR
It consists of a very simple binary that allows you to create notes of size “SMALL/MEDIUM/LARGE” with or without a footer. You could edit each part of the note (header/text/footer) only once, you could also read the note and you could throw the note to the bin.
The binary had a couple of vulnerabilities: One-time use after free and a read after free.
The exploitation path was to leverage the large bin attack to overwrite stderr and craft custom structures to exploit House of Paper via fflush(stderr) (refer to my previous post on it) to gain shell.
Note: There were multiple other posibilities regarding FSOP techniques/houses. I did limit the use of House of Apple 2 by substituting the spaces with underbars.
Challenge overview
It is a typical heap notes challenge. The challenge wasn’t stripped and had the following protections:
When executing the binary you are presented with the following menu:
As after a day of CTF the challenge’s source code was uploaded to ease the resolution of it, I will show it here too.
It is a very simple binary where you are allowed to create, edit, read and throw a note.
When creating a note, you have the posibility to create a maximum of 4 notes with three different sizes 0x410, 0x420, and 0x430, with the possiblity to add a footer of size 0x18 to the note (every note has a header. The header occupies the first 0x20 of every note).
There are a couple of vulnerabilities:
In the read_note function.[1] Only the note’s text is censored if the note has been previously freed, but the header and footer (if exists) are printed. This allows a read-after-free primitive that allows us to leak libc and heap pointers.
In the edit_note function. [2] This if checks whether the note has been initialized yet, but not if it has been freed which allows a UAF.
[2.1] The loop only allows you to edit each part of the note once. This limits the UAF to be used just once.
With these primitives we are ready to gain RCE!
First part: Large bin attack
The notes’ sizes were specifically designed to execute the large bin attack. I will leverage it to overwrite stderr with idea of using FSOP in the next step.
Here is a step by step process of the attack and diagrams that show the heap layout:
Allocate a medium size note (chunk0) with footer
Allocate a small size note (chunk1) with footer
Free chunk0 (goes to unsorted bin)
Allocate big size note (chunk2) without footer (chunk0 goes to large bin)
At this point you could read the header of the note0 which would leak a libc address (first two note header values -> corresponding to the address of the large bin) and a heap address (last two header values -> corresponding to chunk0 address - 0x10)
Edit chunk0 to put &stderr - 0x20 in the fourth field of the header (offset 0x18 which corresponds to bk_nextsize)
Free chunk1 (goes to the unsorted bin)
Allocate last big size note (chunk3) without footer (chunk1 goes to large bin)
The attack is executed and the address of stderr is overwritten with the address of chunk1-0x10 (the address of the header of the chunk). Here, it is also possible to leak the address of chunk1 by reading note0 again (corresponds with the first field fd).
Second part: FSOP to gain shell
As stated before, the challenge was designed to not allow the use of house of apple by banning spaces (house of apple needs " sh" to bypass the conditions). This was done by substituting whitespaces with underbars starting from the second whitespace on (the reason for this is explained at the end of this post).
I will use “house of paper” technique, but any other IO FILE technique that doesn’t require two consecutive whitespaces to be written would also work fine.
Some of the conditions needed for house of paper to work were automatically fulfilled thanks to the values I initialized the notes to (e.g. by filling the fields with different letters in increasing order _wide_data->_IO_write_base < _wide_data->_IO_write_ptr is automatically fulfilled) so I had to change less values when editing the notes.
Note: when executing the payload, it crashed with some mutex functionality because it needed the value of _IO_stdfile_2_lock to be set in the member _IO_FILE_->_IO_lock_t, so as I had a libc leak I could simply set it.
Here is the final heap layout (with offsets relative to the _IO_FILE_plus structure) after crafting the chunks:
Exploit
And here is the final exploit using pwntools:
Why substituting spaces starting from the second?
For the most curious ones reading this post, you may have noticed that the function clean_witespaces doesn’t substitute the first whitespace:
As I have said before, I didn’t want people to use House of Apple because it is very documented and known and I wanted them to at least use one of the AngryFSOP techniques (after the CTF I realised this only limited the use of House of Apple2 xd) or find a different path by themselves. But I came across a pitfall with my own exploit.
The whitespace is 0x20 in hex. During the largebin attack, I wanted to overwrite stderr at address 0x406040. The problem was that I had to write target - 0x20 (in this case 0x406020) to successfully execute the attack.
See the problem here?
I couldn’t write the byte 0x20 needed for the largebin attack because I was substituting it with '_'. So (because 0x20 would be the first byte of 0x406020 in little endian) I changed the order in which the index was updated, updating it before substituting the first whitespace.