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.py
file, theD
chunk is represented as thehax
variable. But for simplicity, we will call itD
here.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
A
in 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
0x210
to0x200
), 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
,A
andC
.- 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 chunkD
due 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
/bk
pointers tomain_arena+216
(which, resides in the libc address space). -
Using the leak in the
D
chunk, we are able to calc the address ofsystem
/'/bin/sh'
string and overcome ASLR. -
Then, we create a ‘fastbin-dup’ primitive by:
free
ing 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
malloc
return 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@libc
with 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/sh
string.
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
rax
register to beSYS_sigreturn
by sending0xf
bytes to the program’sread()
- overflowing into the stack return address in order to jump to a
syscall
gadget - crafting a sigreturn frame, which will trigger another jump to the
syscall
gadget - on the 2nd syscall, we call
mprotect
to modify the permissions of the.text
segment torwx
- then, we jump back to
main
to 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.