vuln-storage (499 pts)
A Heap exploitation challenge.
We were given a target binary which:
- Has all protections enabled(NX/Canary/Full RELRO/PIE)
- Perform size checks that prevents you from OOB-write (except for a small off-by-one bug when copying a nullbyte terminator of a string on the heap)
- UAF mitigations / no dangling pointers to free’d chunks.
Useful reading
ptmalloc/glibc allocator pwnables are kinda new to me. I thought it’d be useful to share the stuff I found while trying to solve this challenge as this can be useful in order to understand the logic behind the technique implemented in the solve.py file:
- Shrinking Free Chunks
- Glibc Adventures: The forgotten chunks.pdf
- fastbin attack漏洞之__malloc_hook攻击
- __malloc_hook - Linux manual page
- Temple of Pwn 7: Heap Fastbin Dup
- Project Zero: The poisoned NUL byte, 2014 edition
- MallocInternals
This one was my favorite chall in the CTF since I don’t have much experience with ptmalloc and it was a good opportunity to gain some hands-on skills :^)
Note: in the
solve.pyfile, theDchunk is represented as thehaxvariable. But for simplicity, we will call itDhere.I also added comments so it will be easier to follow.
Solution
-
We allocate 3 chunks:
A(0x70),B(0x210) andC(0x100). Then, we freeB. -
The off-by-one bug gives us the abillity to overflow past
Ain memory and land a nullbyte, which will corrupt the size field of a free chunk(B) on the heap.- We abuse the overflow to shrink a free chunk(from
0x210to0x200), followed by multiple smaller allocations(B1,B2) which will occupy part of the corrupted free’d chunk space(B).
- We abuse the overflow to shrink a free chunk(from
-
Then, we free
B1,AandC.- ptmalloc never updated
C’sprev_size, this will trigger backward consolidation betweenA,B(with the original size 0x210) andC.
- ptmalloc never updated
-
After having a successful consolidation between the chunks: we allocate one big chunk with the size of
A+B+C(we will call itD) which ends up overlappingB2. At this point we yield a UAF(user-after-free) every time we edit/view chunkDdue to the overlap.Now, we are able to leak libc addresses because
B1(which we previously free’d) belongs to the unsorted bin. And this bin, free chunks on the heap holdsfd/bkpointers tomain_arena+216(which, resides in the libc address space). -
Using the leak in the
Dchunk, we are able to calc the address ofsystem/'/bin/sh'string and overcome ASLR. -
Then, we create a ‘fastbin-dup’ primitive by:
freeing the inner/smaller chunk(B2)- We corrupt the 0x70 freelist by editing the overlapping ‘parent’/bigger chunk(
D).
-
By poisoning the freelist with a forged pointer of our choice, we make
mallocreturn any address we want in the next allocations, hence, achieving a write-what-where abillity. We’ll choose to write to__malloc_hook.- We replace the value in
__malloc_hook@libcwith the addresssystem@libc
- We replace the value in
-
finally, pop a shell by giving the binary a command to allocate a new chunk with a size argument that points to a
/bin/shstring.
Combining everything together:

flag:
TamilCTF{Th3_1n7en7eD_S0lu7i0N_W4S_70_Cr347e_0v3rl4PP1Ng_ChuNkS_bY_h0uSe_0f_3iNh3rj4r_M37h0d_0R_by_P0is0N_NuLL_By73S_4nD_7urN_7h47_70_F4S7BiN_DuP_70_C0D3_3x3cuTi0N}
thanks for the challenge :D
stress-rope (341pts)
Classic SROP challenge (Sigreturn Oriented Programming).
Obstacles: the binary is very small, and it does not include libc / other useful libraries to jump to.
- first, we adjust the
raxregister to beSYS_sigreturnby sending0xfbytes to the program’sread() - overflowing into the stack return address in order to jump to a
syscallgadget - crafting a sigreturn frame, which will trigger another jump to the
syscallgadget - on the 2nd syscall, we call
mprotectto modify the permissions of the.textsegment torwx - then, we jump back to
mainto create another overflow:- we overflow with our shellcode
- change the return address to be where the shellcode is placed.
nameserver (214pts)
Obstacles: libc is randomized, the binary gets an input once & exit immediately(in other words, you have one shot).
./solve.py: This one was a straight-forward OOB write on the stack. We were required to construct a ROP chain to leak libc address, followed by another ROP to create a ret2libc attack.