From the functions defined and the challenge name, this challenge seemed to be written in Free Pascal. Didn’t really invest much time into reversing it, but just assumed, that it will be some use-after-free kind of vulnerability and just went on dynamically debugging it.
And from some quick tests, it seemed that this was indeed the case.
It seems that for the freed pointer we don’t control the LSB (only bytes 1-6), so we don’t have “full” control over it when overwriting it, but we can use it to shove the next chunks around a bit…
Since we can edit the freed chunk again, we can just overwrite the next free pointer in the chunk and let it point somewhere else.
I thought about overwriting some exitfuncs kind of structure and after testing out some of the global function pointers, I stumbled across U_$SYSTEM_$$_STDOUT as a working target.
We can allocate another chunk into this and overwrite the function pointers in it. One of those should get triggered when the challenge exists and tries to flush stdout.
The next chunk would now get allocated into the stdout structure. Let’s see, what will happen, if we just fill it up with garbage.
Looking good, so we control the value at rbx+0x38. rdi will point to stdout+0x8 at this point.
We just need a fitting gadget to pivot the stack into our allocated chunk and also to change rdi to point to a more controllable address.
Perfect :)
This will set rdi to [rdi+0x28] and rsp to [rdi+0x30] and then jump to [rdi+0x38]. And we control all of those values.
Flushing stdout will now trigger our stack pivot, which will set rdi to 0x483618 (the address where we put the /bin/sh string), set rsp to 0x483660, where the content of our chunk is stored (payload2) and then call rdi+0x38, which is pop rax; ret.
Since we moved rsp to the beginning of content, this will now set rax to 59 (execve) and then clear rsi and execute execve("/bin/sh", 0, 0).