tl;dr
- Overwrite
mmap_threshold
with null and trim top chunk size. - Null out last 2 bytes of stdin’s
_IO_buf_base
and brute force to get allocation on stdin. - Overwrite one of the jump tables with win function to get shell.
Challenge Points: 453
Solves: 4
We had a really great time this weekend with this year’s edition of Alles CTF. I spent most of my time working on the challenge nullptr and in this post , I’ll be discussing the intended solution for the challenge.
PS: We could not solve this during the CTF but the exploit idea is worth sharing.
Challenge description
To begin with , we’d been provided with a pretty simple c source code which has 2 functionalities.
1 |
|
- The view function prints the content of an address passed , hence we can be assured of all leaks ;).
- The nuke function nulls out the content of an address passed.
Looks pretty simple doesn’t it?
Getting necessary leaks
Getting all required leaks is nothing but a trivial task.
Initially, we can directly get stack leak by passing any non-numeric
value to scanf. Let’s script it a bit.
1 |
|
Idea of exploitation
After carefully analyzing scanf’s source code, sherl0ck came up with the idea of calling malloc again by nulling
out IO_buf_base
.
In the depths of scanf ,there resides a function called IO_doallocbuf.
1 | if (fp->_IO_buf_base == NULL) |
The code is actually of the caller function of _IO_doallocbuf
which is _IO_new_file_underflow
.
It calls malloc with a fixed size of blk_sizet
which is by default 0x1000 bytes.
From this point on , we were stuck , we tried nulling out the last 2 bytes of buf base in the hope of getting allocation at tcache structure , from there on we faked a 0x400 size arbitrary chunk in tcache and found another way to call malloc with size of 0x400 from stdout structure.
Well , getting allocation with stdout doesn’t actually give us arbitrary write.
The intended solution
Well , the intended solution is actually leveraging an mmap call from malloc. Let’s see how this can be done.
- Nulling out
mmap_threshold
with triggers a different code path in malloc. - Also , trimming top size by writing null misaligned finally calls mmap when malloc is invoked.
Now , all that we have to do is , brute force until an mmap happens near our stdin file structure and from there on , its a game over.
Let’s take our script forward.
1 | buf_base = libc + 0x1ea9b8 |
Once there’s a hit , all that’s left is to partially overwrite IO_buf_base
and get allocation on stdin. Here , after getting allocation on stdin , we intend to overwrite malloc hook to get shell.
1 | #Now we partially overwrite io buf base of stdin |
An alternative approach
We could be all lazy and let brute force do the work. A simpler yet time consuming approach would be to overwrite the last 3 bytes of stdin’s IO_buf_base
and wait for the magic to happen. Eventually , in one of the runs , it would match with binary bss and you get a direct write to GOT table.
Conclusion
The challenge had really intersting concepts involved and we learnt quite alot. Kudos to the author Mrmaxmeier for the awesome challenge.
Here’s the original script of the author - Link