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

【学习记录】Week4(四):进阶栈溢出——ret2syscall、栈劫持与 ret2mprotect 实战

写在前面:在前三篇文章中,我们掌握了 ret2libc 泄露、基础 ROP 拼接以及大杀器 ret2csu。但 CTF 的世界总是千奇百怪:如果题目没有给你 libc,或者程序里根本找不到syscall指令怎么办?如果溢出的空间极其狭小,连 ROP 链都放不下怎么办?本文作为 Week4 的收官,将带你学习三种高阶栈溢出技巧,彻底补全你的基础攻击面。

📑 目录

  1. 越过 libc:ret2syscall 直接调 execve
  2. 空间魔术:栈劫持与 fake stack frame 构造
  3. 破除禁锢:ret2mprotect 修改内存权限
  4. Week4 总结与进阶展望

1. 越过 libc:ret2syscall 直接调 execve

场景痛点:有时候题目是静态编译的,或者没有输出函数让我们泄露 libc 基址。此时 ret2libc 走不通。
破局思路:既然程序静态编译了,那它内部大概率包含了syscall指令。我们不需要调用system函数,而是直接通过 ROP 链布置寄存器,最后执行syscall指令,直接触发操作系统的execve("/bin/sh", NULL, NULL)系统调用。

64 位 execve 系统调用约定:

  • rax= 0x3b (59,execve 的系统调用号)
  • rdi= “/bin/sh” 字符串地址
  • rsi= 0 (argv)
  • rdx= 0 (envp)
  • 最后执行syscall指令

假设性说明(模拟 Gadget 查找):
使用 ROPgadget 查找静态编译的程序:

ROPgadget --binary vuln --only "pop|ret" | grep rax # 模拟输出: 0x0000000000401b8f : pop rax ; ret ROPgadget --binary vuln --only "pop|ret" | grep rdi # 模拟输出: 0x0000000000401b93 : pop rdi ; ret ROPgadget --binary vuln --string "/bin/sh" # 模拟输出: 0x00000000006c1000 : /bin/sh ROPgadget --binary vuln --only "syscall|ret" # 模拟输出: 0x0000000000401b95 : syscall ; ret

(假设我们通过 ret2csu 或其他方式已经将rdxrsi置零了)

ROP 链拼接推演:

payload = b'A' * offset # 假设已经处理了 rsi 和 rdx 为 0 payload += p64(pop_rdi) + p64(bin_sh_addr) payload += p64(pop_rax) + p64(0x3b) payload += p64(syscall_addr)

通过这种方式,我们完全绕过了 libc,直接让 CPU 替我们执行系统调用。

2. 空间魔术:栈劫持与 fake stack frame 构造

场景痛点:有时候程序的溢出点非常小,比如read(0, buf, 0x20),除去 16 字节的填充和 8 字节的返回地址,我们只剩下 8 字节的空间,根本塞不下完整的 ROP 链。
破局思路:既然当前的栈空间不够,那我们就把 ROP 链写到其他地方(如 BSS 段),然后把栈指针(RSP)劫持过去

核心指令:leave; ret
回顾汇编知识,leave指令等价于:

mov rsp, rbp ; 把 rbp 的值赋给 rsp pop rbp ; 把此时栈顶的值弹入 rbp

如果我们能控制rbp的值,然后执行leave; ret,就能瞬间改变rsp的指向!这就是“栈迁移”或“伪造栈帧”的核心。

假设性实战推演:
假设溢出偏移为 16(即 16 字节后覆盖到 rbp),我们只能控制rbp和返回地址。我们的目标是把栈迁移到bss_addr

  1. 第一次溢出时,我们在bss_addr提前写好完整的 ROP 链。
  2. 构造第一次的 Payload:
    • rbp被覆盖为bss_addr - 8(因为leave中的pop rbp会弹出一个 8 字节,我们需要让执行完pop rbp后,rsp正好指向bss_addr)。
    • 返回地址覆盖为程序中某个leave; ret指令的地址。

栈结构设计:

低地址 | 16字节填充 | | bss_addr - 8 (覆盖 rbp) | <- 此时原 rbp 被篡改 | leave_ret_addr (返回地址)| <- ret 跳到 leave; ret 执行 高地址

当程序执行原本的leave; ret(或者跳到我们自己构造的leave; ret)时:

  1. mov rsp, rbp->rsp变成了bss_addr - 8
  2. pop rbp->rsp变成了bss_addr,此时栈顶就是我们提前写好的完整 ROP 链!
  3. ret-> 开始执行我们在 BSS 段布置的 ROP 链。

这种技术在空间极其狭小的栈溢出中是起死回生的神技。

3. 破除禁锢:ret2mprotect 修改内存权限

场景痛点:程序开启了 NX 保护,栈和 BSS 段都不可执行。我们想用 Shellcode,但系统不让执行。
破局思路:Linux 提供了mprotect函数,可以修改内存页的权限。如果我们通过 ROP 调用mprotect(bss_addr, 0x1000, 7),就能把 BSS 段改成可读可写可执行(rwx,7 = 4+2+1),然后跳过去执行 Shellcode。

函数原型:int mprotect(void *addr, size_t len, int prot);

  • addr必须是内存页的整数倍(通常是 0x1000 对齐,如0x404000
  • len是长度
  • prot是权限,7 代表PROT_READ | PROT_WRITE | PROT_EXEC

攻击步骤规划:

  1. 使用 ret2csu 或基础 ROP,控制rdi = 0x404000rsi = 0x1000rdx = 7
  2. 调用mprotect
  3. 接着调用read(0, 0x404000, 0x100),将我们的 Shellcode 读入 BSS 段。
  4. 最后ret跳转到0x404000执行 Shellcode。

假设性说明(模拟 ROP 链结构):

payload = b'A' * offset # 1. 调用 mprotect(0x404000, 0x1000, 7) payload += p64(pop_rdi) + p64(0x404000) payload += p64(pop_rsi) + p64(0x1000) # 假设 rdx 已经是 7,或者用 csu 控制 payload += p64(mprotect_addr) # 2. 调用 read(0, 0x404000, 0x100) 把 shellcode 读进去 payload += p64(pop_rdi) + p64(0) payload += p64(pop_rsi) + p64(0x404000) payload += p64(pop_rdx) + p64(0x100) payload += p64(read_addr) # 3. 跳转到 0x404000 执行 payload += p64(0x404000)

read读取完我们发送的 Shellcode 后,ret指令会精准跳到 BSS 段,此时该区域已是rwx权限,Shellcode 顺利执行。

4. Week4 总结与进阶展望

至此,Week4 的四大模块全部完成!
从最基础的ret2libc 泄露与劫持,到拼接任意函数调用的基础 ROP;从解决 64 位传参痛点的ret2csu,到直接越权系统调用的ret2syscall;再到空间魔术般的栈劫持和改变内存属性的ret2mprotect

如果说 Week1-Week3 是让你认识了 PWN 的武器库,那么 Week4 就是教你如何组合这些武器,形成一套完整的攻击体系。掌握了这些,常规的栈溢出题已经很难挡住你的脚步。

下周预告 (Week5)
栈上的基础利用我们已基本讲完。但在实际环境中,栈上的保护(Canary)越来越严密。下周我们将正式踏入的世界,从mallocfree的底层实现讲起,揭开Use-After-Free (UAF)Double FreeTcache机制的神秘面纱。堆利用才是现代 PWN 的主战场!

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

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

相关文章:

  • Selenium自动化测试入门:从环境搭建到框架集成的完整指南
  • 5分钟搭建Sunshine:零门槛打造你的免费游戏串流主机
  • 微电网控制柜主控模块七大核心功能,决定微网智能化上限
  • Python从入门到实战(一):初识Python与基础语法
  • 华为手机地震预警全面升级,提前开启筑牢“安全防线”
  • 为什么远程分支删除了,本地 git branch -r 还能看到?
  • 开发者必读:Kiran-authentication-devices的驱动工厂与设备创建机制
  • 收藏!小白程序员也能抓住的AI高薪机遇,大厂都在布局!
  • 企业微信外部群API 项目中的文件与素材回调如何处理
  • xshell与xftp的连接教程
  • 无蜂窝大规模MIMO无线前传系统与硬件损伤优化
  • 从“AI 辅助”到“AI 代理”:2026 年 IDE 智能化演进的三个关键阶段
  • 2026中日跨境 + 全国多点位,SAP运维服务商综合能力盘点
  • 拯救者笔记本终极控制神器:Lenovo Legion Toolkit完全指南
  • 抖音下载器终极指南:快速免费保存无水印视频
  • 朗艺琴行课程体系|从启蒙到专业,一站式音乐成长
  • 音频功放国产化:NX8403 3W D类功放,兼容PAM8403/CS8403/NS8403
  • uniapp APP端实现NFC读卡功能
  • VS Code 插件市场 AI 类插件上架量暴增 6 倍:2026 年开发者工具链选型避坑指南
  • 同网段与不同网段通信及vlan
  • Spring Boot安全实战:防范路由暴露、SQL注入与Thymeleaf SSTI三大核心漏洞
  • 2026 OpenClaw (小龙虾) 全能安装与配置指南
  • Brand Mind用RAG压测100次AI态度变化
  • AI编程工具与数据标注平台实战解析
  • 3分钟终极指南:用ncmdumpGUI轻松解密网易云NCM音乐文件
  • STM32与PCF8591实现多通道ADC/DAC信号转换方案
  • 如何通过tModLoader将你的泰拉瑞亚创意变为现实
  • AI系统故障诊断与智能运维实践指南
  • 《HarmonyOS技术精讲-ArkWeb》安全防线:隐私保护与沙箱机制
  • 如何免费解锁Wand专业版:开源增强工具让你的游戏修改体验更完美