This time I want to share a challange from last weekend’s QWB game, which only allows Chinese team to participate. This particular challenage
one, I did not have time to look during the game, but when I solved it, I think it is worth to share. You can download the binary here.
No surprise, the binary was compiled with full protection.
It is a typical menu program with
delete, and the organizor also provided a libc of
I soon identify the vulnerability, it locates in
unsigned __int64 edit()
we can edit the string content char by char, and the
strchar could lead to heap overflow. Well, seem not difficult at all, with the
tcache poisoning technique, it should be easy enough to solve. However, there is an address sanitization at
check_addr is like:
__int64 __fastcall check_addr(__int64 addr)
HIGH is initialized when the program start,
LOW is the heap address of very first allocation, and
HIGH = LOW + 0x5f0.
Oh, it also provided a secret function, which can leak a code address. The leak is a little bit obscure, I’ll spare this part for the readers to explore.
If we fill all 20 slot, the bss layout is like following memory dump:
pwndbg> tele 0x555555757040 0x60
0x555555758850, so we can
malloc can only return addresses within this range, otherwise, the session died. I considered this as extra mitigation towards
We’ve got a code address, a heap overflow. With heap overflow, we can modify
0x555555758350’s content and overflow it to
0x555555758390’s chunksize, change it from
0x441, then we delete it and got a chunk in unsortedbin. This allows us to leak
libc address and create overlapping chunk, overlapping chunk also helps us to get
heap address. So we can have
heap address, enough for bypassing
PIE. What next?
¶Unsortedbin Attack in Glibc 2.27
A straightforward idea is to unlock the address sanitization by overwriting
HIGH variable, with unsortedbin attack. I mentioned unsortedbin attack can be very useful in previous writeup, and how2heap concluded this technique can only apply to glibc without tcache feature, which is < 2.26. But is this true?
After some searching, I found CTF-All-In-One proposed a method. It provided a poc:
In short, we can change
tcache->counts[tc_idx] == 0xff by doing tcache poisioning, and it will get into else statement in following code snippet, which will return directly instead of getting into another round. Noting that
mp_.tcache_count = 7 by default.
Sound good, we can following the steps in poc and do unsortedbin attack against
HIGH, in order to unlock the address check mitigation. But once we turn
tcache->counts[tc_idx] == 0xff, it means we can no longer utilize this tcache bin, any free chunk of this size(0x40) will get into fastbin. In this challenge,
0x40 is the only size we can allocat, and there is no such fake size around regular control-flow hijack target like
I want to enable tcache again after unsorted bin attack, so we can perform tcache poisoning easily. I looked into the source of
Our target is getting into the if statement on line 9, and the key is to make
tcache->counts[tc_idx] < mp_.tcache_count become true. What is
pwndbg> p mp_
It is a structure in
libc, which contains some information of the tcache status. And I spotted there is a fake size 64(0x40) in the structure, right before
mp_.tcache_count. We can use fastbin attack to modify
pwndbg> p &mp_.tcache_count
Our target is to make
tcache->counts[tc_idx] < mp_.tcache_count become true,
tcache->counts[tc_idx]=0xffffffffffffffff(-1) now, while the comparison is unsigned, we cannot found any
tcache_count to fullfill the condition. However, we can make
mp_.tcache_count=0xffffffffffffffff(-1). Then we are good to use tcache again.
Here is the exploit plan:
- leak code address
- leak libc address
- leak heap address
- tcache poisioning to make
tcache[idx]->count = -2
- unsortedbin attack aginst
- fastbin attack against
mp_.tcache_count, make it
- tcache poisioning against
The final exp, this is constructed when ASLR is off, some adjustments is necessary for remote target:
from pwn import *
In this writeup, we can see how to perform unsortedbin attack to overwrite a certain variable in glibc 2.27, and recover the tcache feature afterward. I think that the
0x40 size is carefully chosen by the designer, helpping us to get a foothole on
mp_ structure. The writeup from r3kpig use another method to change
HIGH variable, and this post mentioned the smallbin cache filling mechanisum can also achieve same effect.