- Leak with Format String bug.
- Use the arbitrary heap pointer write to overwrite
- Inject shellode in heap and get code execution in
Challenge Points: 388
We really had a great time this weekend playing this year’s edition of TokyoWesterns CTF. In this post I’d like to share the intended solution for the challenge Smash which we could not solve during the CTF but the idea and the concept involved is worth sharing.
To begin with , we’ve been provided with the challenge binary ,
libc 2.31, a runner bash script and a folder containing Intel’s tool called Control-flow Enforcement Technology (CET).This was our first tackel with Intel’s CET and the concept involved is truly worth sharing.
All mitigations except canary have been enabled.
- The input name function takes 0x20 bytes of user input and then does an
strdupwhich stores our input on heap.
- Next ,
dprintfis called on our input without any format specifier, hence we can leak required addresses with the format string bug.
- After this , we’re asked to enter
yis entered , the program further asks for another
input messageand then takes in another 0x38 bytes of input.
nis entered , the program prints
Byewith dprintf and directly exits.
Pretty straight forward, but where are the bugs?
We actually have 2 overflows which let us corrupt rbp and pivot to almost anywhere, but wait, CET doesn’t allow us to execute ROP chain directly. We have to find a way to get code execution.
Control-Flow Enforcement Technology promises to guard a binary against attacks such as ROP, JOP etc. It does so by allocating a
shadow stack in mapped memory region. Whenever a function is called, apart from storing the return address on the program’s thread stack , it also stores it on the shadow stack. So whenever the program returns from the function, the return address is comapared with the one stored on the shadow stack , if a match is found, the program executes smoothly, and if not , the program aborts thus mitigating ROP.
Intel SDE provides an emulation that includes:
- Stack checks
- Indirect branch checks
From the above discussion, one thing gets clear , every function that is executed in the supervision of CET needs to begin with
endbr64. Let’s just bear that in mind and continue.
Since we have our required leaks , we can now corrupt rbp with our first overflow in the step where program asks us to enter
An important observation to be made here is that , after reading our input and storing on heap with strdup, the program copies the heap pointer to an offset of
Since , we overwrote rbp with an address of our choice , after the function executes
leave , the rbp will be updated with the value that we specified.
Immediately after that, the following instructions copy the heap pointer storing our input to
mov QWORD PTR [rbp-0x8],rax
Thus , we have an arbitrary write of a heap pointer.
After analyzing a bit , we found out that the emulator that the CTF has provided us with does not check for the
NX bit and few pages have been marked
read-write-executable allowing us to now inject shellcode.
Now that we can execute shellode, we have to now select a target to get code execution.
One important aspect of dprintf is that it uses a
temporary file structure to carry out it’s operations. With file structure operations, we can find out apt targets to get code execution. One such target function pointer is
_IO_new_file_finish which is called internally inside
So , now our plan is :
- Overwrite rbp to point to
_IO_new_file_finish + 8.
- Copy heap address to
- Fill heap with shellcode as CET doesn’t implement NX.
- Get code execution in dprintf.
Here’s the full exploit code.
Remember that the shellcode has to be started with
endbr64 instruction to bypass the indirect branch instruction check.
[+] Opening connection to pwn01.chal.ctf.westerns.tokyo on port 29246: Done
This was one of the most interesting challenges I had come across in a while. The idea of a faulty emulator and CET was really cool. Kudos to team TokyoWesterns for such a cool challenge and such an awesome CTF.