tl;dr
- Leak with Format String bug.
- Use the arbitrary heap pointer write to overwrite
__GI__IO_file_jumps
. - Inject shellode in heap and get code execution in
dfprintf
.
Challenge Points: 388
Solves: 9
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.
Challenge description
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.
1 | gdb-peda$ checksec |
All mitigations except canary have been enabled.
Reversing
- The input name function takes 0x20 bytes of user input and then does an
strdup
which stores our input on heap. - Next ,
dprintf
is 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
y
orn
and ify
is entered , the program further asks for anotherinput message
and then takes in another 0x38 bytes of input. - If
n
is entered , the program printsBye
with dprintf and directly exits.
Pretty straight forward, but where are the bugs?
Exploit development
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.
How Intel’s CET works
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.
In search of arbitrary write
Since we have our required leaks , we can now corrupt rbp with our first overflow in the step where program asks us to enter y
or n
.
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 rbp-0x8
.
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 rbp-0x8
.
1 | mov QWORD PTR [rbp-0x8],rax |
Thus , we have an arbitrary write of a heap pointer.
Defeating CET
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.
dprintf to the rescue
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 dprintf
.
So , now our plan is :
- Overwrite rbp to point to
_IO_new_file_finish + 8
. - Copy heap address to
_IO_new_file_finish
. - Fill heap with shellcode as CET doesn’t implement NX.
- Get code execution in dprintf.
Here’s the full exploit code.
1 |
|
Remember that the shellcode has to be started with endbr64
instruction to bypass the indirect branch instruction check.
1 | [+] Opening connection to pwn01.chal.ctf.westerns.tokyo on port 29246: Done |
Conclusion
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.