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

【学习记录】Week6(四):黑暗中起舞——BROP 盲打利用无二进制场景突破

写在前面:在之前所有的实战中,我们都有一个前提——手里有题目对应的二进制文件(ELF),可以在 IDA 里看伪代码,用 ROPgadget 找 Gadget。但如果题目只给了一个远程 IP 和端口,二进制文件完全未知,甚至运行在远程服务器上,我们该怎么办?这就是 PWN 的终极盲打技术:BROP(Blind Return Oriented Programming)。今天,我们将戴上夜视仪,在完全没有源码的黑暗中,仅靠程序的“崩溃”与“存活”反馈,一步步摸出 ROP 链并拿 Shell。

📑 目录

  1. 极致绝境:没有二进制文件的盲打
  2. 核心思想:基于侧信道的“崩溃探测法”
  3. 寻路指南针:寻找 Stop Gadget 与 BROP Gadget
  4. 破局关键:定位puts与泄露 libc 地址
  5. 终极构造:Dump 内存与完整 ROP 链
  6. Week6 总结与进阶展望

1. 极致绝境:没有二进制文件的盲打

BROP 攻击场景通常出现在远程服务(如 nginx、Apache 或某个默默运行的守护进程)存在栈溢出,但我们拿不到固件。
我们唯一能做的就是向远程发送数据,并观察 TCP 连接是否断开:

  • 程序崩溃:连接突然断开(EOF)。
  • 程序存活:连接保持,甚至有正常回显。

因为程序通常是使用fork创建子进程处理连接的,所以即使子进程崩溃,父进程依然存活,Canary 等保护机制在这里反而不是阻碍,反而因为进程不死,让我们可以无限次试错。

2. 核心思想:基于侧信道的“崩溃探测法”

BROP 的核心逻辑是“试错”。
假设我们向buf填入大量字符导致程序返回到一个随机的非法地址,程序崩溃。
如果我们把返回地址换成一个合法的代码地址,程序可能不会立刻崩溃,而是继续执行该地址处的代码。

通过遍历地址空间,发送[Padding] + [探测地址],观察连接是否断开,我们就能在黑暗中找到有用的指令地址。

3. 寻路指南针:寻找 Stop Gadget 与 BROP Gadget

在盲打中,我们不能随便跳转,因为跳转过去的代码如果包含ret,可能会破坏我们后续的栈结构导致崩溃。我们需要找特定的 Gadget。

3.1 寻找 Stop Gadget(停止小工具)

我们需要找一个“坑位”,让程序跳过去后既不崩溃,也不返回,而是卡住或循环。这通常是sleep函数或while(1)循环。
探测逻辑:
发送[Padding] + [探测地址] + [大量垃圾数据]
如果程序没有崩溃断开(连接保持),说明探测地址就是一个 Stop Gadget。它把后续的垃圾数据吞掉了。这个 Stop Gadget 是我们后续探测的“保命符”,跳到它就不会崩。

3.2 寻找 BROP Gadget(万能跳板)

在 64 位中,最常用的 Gadget 是__libc_csu_init尾部的 6 个pop加 1 个ret
pop rbx; pop rbp; pop r12; pop r13; pop r14; pop r15; ret

探测逻辑:
发送[Padding] + [探测地址] + [6个有效地址] + [Stop Gadget]
如果探测地址是 BROP Gadget,它会消耗掉栈上的 6 个地址,然后执行ret跳到我们的 Stop Gadget,程序存活!
如果不是,栈结构错位,程序大概率崩溃。通过这种方式,我们在茫茫内存中盲猜出这个 7 连发 Gadget 的地址。

4. 破局关键:定位puts与泄露 libc 地址

有了 BROP Gadget,我们其实就拥有了pop r15; ret(取 BROP Gadget 地址 + 7 偏移即可得到pop r15; ret,但通常我们直接用它推导出pop rdi; ret,即 BROP Gadget 地址 + 9)。
现在我们有了pop rdi; ret,可以控制第一个参数了。接下来要找输出函数。

4.1 盲找 PLT 表

PLT 表的每一项通常是 16 字节,结构固定。我们可以通过遍历某个可能的地址范围,尝试调用它。

4.2 定位puts@plt

我们怎么知道遍历到的是puts还是printf
我们可以把rdi设置为某个 GOT 表项的地址(比如 BROP Gadget 所在页的某个固定偏移,那里大概率有.dynamic段的魔数\x7fELF)。
如果我们调用某个 PLT 项后,远程返回了\x7fELF这样的字符串,说明我们找到了puts(或write)!

假设性盲打脚本推演:

from pwn import * # 假设我们已经找到 offset=72, brop_gadget=0x4007ba, stop_gadget=0x40055c # pop_rdi = brop_gadget + 9 # 尝试探测 PLT 表项 # 假设从 0x400500 开始探测 for addr in range(0x400500, 0x400600, 0x10): try: p = remote('127.0.0.1', 8888) payload = b'A' * 72 payload += p64(pop_rdi) # 弹出参数给 rdi payload += p64(0x400000) # ELF 文件头地址,必定有 \x7fELF payload += p64(addr) # 探测的 PLT 地址 payload += p64(stop_gadget) # 保命 p.sendline(payload) response = p.recv(timeout=1) # 如果收到 \x7fELF,说明找到了 puts if b'\x7fELF' in response: log.success(f"Found puts@plt at: {hex(addr)}") puts_plt = addr p.close() break p.close() except: p.close() continue

模拟终端输出:

[+] Opening connection to 127.0.0.1 on port 8888: Done [+] Found puts@plt at: 0x400535

5. 终极构造:Dump 内存与完整 ROP 链

找到puts@pltpop rdi; ret后,我们就从“盲打”回到了“明打”!

5.1 泄露 libc 地址

我们可以让puts打印puts@got里的内容,从而泄露 libc 基址。
但盲打中我们不知道puts@got在哪。
破局思路puts@got通常在puts@plt附近。我们可以利用puts@plt里的第一条jmp [got_addr]指令,反推 GOT 地址;或者直接盲扫 BSS 段后面的 GOT 表,把内容打印出来,直到发现像 libc 地址(以0x7f开头)的数据。

5.2 Dump 二进制文件

既然有puts,我们可以把整个程序的.text段从头到尾打印出来,保存到本地文件,反编译成 ELF。这样我们就“偷”回了二进制文件!

# 假设已经拿到了 puts_plt 和 pop_rdi dump_addr = 0x400000 dump_data = b'' while dump_addr < 0x401000: payload = b'A' * 72 payload += p64(pop_rdi) payload += p64(dump_addr) payload += p64(puts_plt) payload += p64(stop_gadget) # 保持存活 p.sendline(payload) # puts 遇到 \x00 会截断,需要特殊处理补齐 leak = p.recv(timeout=1) dump_data += leak dump_addr += len(leak) # 粗略推进

拿到完整的 ELF 文件后,接下来的操作就是常规的 ret2libc:找到system/bin/sh,构建最终 Payload 拿 Shell。

6. Week6 总结与进阶展望

至此,Week6 的进阶栈溢出之旅圆满结束!
本周我们从栈迁移(突破空间限制)开始,掌握了SROP(突破寄存器限制),学习了ret2dl_resolve(突破无 libc 泄露限制),最后在BROP中体验了在完全没有二进制文件的黑暗中重建光明的极致盲打。

这些技术不再是简单的套公式,而是深刻理解了操作系统、编译器与底层汇编机制后的“魔法”。栈溢出到这里,基本上已经没有更多的新花样了。

下周预告 (Week7)
栈上的厮杀彻底告一段落。从下周起,我们将正式踏入现代 PWN 的主战场、也是最容易让人劝退的领域——。我们将从glibc的内存管理机制讲起,揭开mallocfree的底层面纱,学习Use-After-Free (UAF)Double Free以及最经典的Fastbin Attack。堆的世界更加复杂,但也更加精彩!

如果 Week6 的系列文章对你的学习有帮助,请点赞收藏支持!你的鼓励是我持续更新的最大动力。我们 Week7 见!🙏

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

相关文章:

  • SPI EEPROM与PIC微控制器的嵌入式数据存储方案
  • 如何在原神中轻松解锁120帧:终极帧率解锁指南
  • Pandas中.loc与.iloc核心区别:标签索引vs位置索引
  • 15A大电流FOC无刷电机控制方案设计与实现
  • dify 的基本使用
  • SuperPNG终极解决方案:Photoshop高质量PNG插件深度解析与优化指南
  • 锂离子电池保护芯片BQ2920设计与PIC32协同控制
  • 汽车电子散热系统设计与DRV8213驱动优化
  • KMS智能激活终极解决方案:三步永久激活Windows和Office的完整指南
  • 工业级条码识别系统:LV30扫描头与PIC18F57K42的硬件设计与优化
  • 现在不学ChatGPT做PPT,下周就被淘汰:2024Q2职场技能预警报告(TOP10岗位需求暴增217%)
  • STM32F410RB与AD74413R的高精度信号采集与输出方案
  • STM32L442KC与SLO2016构建工业级低功耗通信方案
  • 抖音评论采集终极指南:如何5分钟搞定3000+评论数据提取
  • WarcraftHelper:魔兽争霸3终极优化指南,解决现代系统兼容性问题
  • KMS智能激活全攻略:三步永久激活Windows和Office的终极方案
  • 基于PIC18F65K40与25CSM04的嵌入式数据存储优化方案
  • KMS激活终极指南:三步永久激活Windows和Office的完整教程
  • LV30扫描头与PIC32微控制器的工业级条码识别方案
  • STM32与M95M02-DR EEPROM的SPI接口设计与优化
  • Proxy 与依赖追踪:Vue3 响应式系统的底层机制剖析
  • 四大连锁收银软件工厂深度横评:商拓、柚子、商琦云与银阁仕实战对比
  • ChatGPT面试训练全链路指南:从简历优化、行为问题拆解到压力测试反馈,9步闭环拿下大厂Offer
  • 3分钟实现离线音乐库智能歌词同步:LRCGET批量歌词下载工具实战指南
  • 厌倦手动换肤的繁琐操作?R3nzSkin国服特供版为你提供一站式自动化解决方案
  • 工业级24V转3.3V电源方案设计与TM4C控制实现
  • 工业级4-20mA电流环技术解析与DAC161S997应用
  • Python 盘口数据校验:bids/asks、timestamp、spread 和信号边界
  • 3分钟掌握LRCGET:批量歌词下载与同步编辑终极指南
  • PIC18F46K80与171010550构建智能DC-DC降压电源方案