西湖论剑 easy_pwn writeup
TOC
一道很经典的pwn题,思路一点都不偏,很适合练手,靶机环境是glibc-2.27。
源程序、相关文件下载:easy_pwn.zip 。
溢出点
在add函数中有个明显的off by one
。
void __cdecl add() |
read_n_sub_A19
void __fastcall read_n_sub_A19(_BYTE *buf, int length) |
思路
- 泄露glibc基地址,修改 index-7 为 no_in_use
- 泄露heap基地址
- 构造fake_chunk
- 通过chunk extend使得chunk重叠
- 控制tcache的fd
- 劫持__free_hook
泄露glibc基地址
原理就是绕过tcache
,利用unsorted bin
上残留的main_arena
的地址信息来计算出glibc的地址
。
for i in range(10): |
add(0xf8, '\n')
的主要功能就是修改index_7
为no_in_use
,为了我们后面的chunk extend
。
main_arena_96_offset 需要根据不同的环境手动计算。
泄露heap基地址
计算heap
的地址和算glibc
的地址有点类似,但是对于绝大多数glibc
来说,这个偏移地址都是固定的。
modify(0, 'd') |
将第一个字符修改为 d 是为了防止,调用puts函数时,被 NULL 截断。
计算方法如下:
先查看一下chunk
布局。
pwndbg> heap |
我们要计算的就是0x55c8eddaa764
和heap
基地址的偏移。
在查看heap的基地址:
pwndbg> vmmap |
0x55c8eddaa000
就是heap
的基地址。
计算偏移:
pwndbg> p/x 0x55c8eddaa764-'d'-0x55c8eddaa000 |
这里的0x700
就是对应脚本中的heap_base_offset
。
构造fake_chunk
由于index_0
指向的chunk和index_7
指向的chunk相邻,所以我们可以在index_0
上伪造一个fake_chunk
。
chunk_for_unlink_addr = heap_base_addr + 0x850 # index 0 |
这里设置 fake_chunk 的 fd 和 bk ,主要是为了绕过 unlink 的 双向链表完整性检查。至于 index 0 相对于 heap base addr 的偏移可以手动算出,而且大部分glibc的该偏移值是固定的。
chunk extend
原理就是先绕过tcache
,然后利用unsorted bin
free时,会向上和空闲的chunk合并的机制,因为我们在前面已经通过off by one
设置了index_7
为no_in_use
,所以可以直接触发该机制。那么执行之后,他就会和我们构造的fake_chunk
合并了。
# Instead of index_0, becasue we will use index_0 to control the fake chunk later |
这里我用index_8
来代替index_0
,因为index_0
之后要用来控制fake_chunk
.
在pause()
出暂停,来看看结果:
pwndbg> bin |
可以看到合并之后size
就是就是0x1f0
。
控制tcache的fd
由于index_0
指向的chunk和unsorted bin
是重叠的,我们可以把unsorted bin
放到tcache
中,然后再控制其fd
。
for i in range(1, 8): |
在pause()
停下看看运行结果:
pwndbg> bin |
可以看到,我们已经可以控制__free_hook
。
劫持__free_hook
add(0, '\n') # index 2 |
index 2
为我们可以控制的chunk,为了防止在delete
函数被memset
函数刷新内容,这里我设置它的size
为 0 ,然后用index 0
来控制它的内容。
至于我为什么不用 one gadget,主要原因是不稳定,而且可移植性很差。
完整脚本
#!/usr/bin/python2 |
运行实例
ex@Ex:~/test$ python2 ./exp.py |
总结
感觉很棒的一道题,考察的都是基础内容的组合,整个题目其实并不难,但是很考验pwn选手的功底。