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

在之前的题目中,我们对抗了栈溢出、Canary 和 PIE 等“硬核”的内存保护机制。而在 PWN 032 中,我们将接触到一种更偏向于“代码审计”与“编译时”的安全机制——FORTIFY_SOURCE

本关作为该机制的入门(Level 0),主要展示了在未开启该保护或逻辑存在后门时,程序是如何被“轻取”的。

pwn 032 危险函数与逻辑后门

题目信息与环境侦察

题目描述

FORTIFY_SOURCE=0:
禁用 Fortify 功能。不会进行任何额外的安全检查。可能导致潜在的安全漏洞。

解题过程: 老规矩,先 checksec pwn 检查程序保护情况。

image-20260125232844610

分析:虽然 PIE、NX 和 Full RELRO 全部拉满,看起来固若金汤,但 No canary 和题目提示的 FORTIFY_SOURCE=0 暗示了这可能是一个关于缓冲区溢出或者逻辑利用的题目。

第一部分:基础概念

1. 什么是 FORTIFY_SOURCE?

  • 官方定义FORTIFY_SOURCE 是 GCC 和 Glibc 提供的一种安全机制,主要用于在编译时运行时防止缓冲区溢出。
  • 通俗理解“带刺的函数”
    • 在 C 语言中,strcpymemcpysprintf 等函数非常危险,因为它们不检查目标缓冲区够不够大,给多少数据就塞多少数据。
    • 开启 FORTIFY_SOURCE 后,编译器会自动把这些危险函数替换成“安全版”(如 __strcpy_chk)。如果程序检测到你要塞的数据超过了缓冲区的长度,它会直接报错并终止程序,根本不给你溢出的机会。

2. 保护等级 (Level)

  • Level 0 (本题)“裸奔”。禁用该功能,不进行额外检查,完全信任程序员的代码。
  • Level 1“初级防御”。在编译时进行检查(如果长度是已知的常量),运行时进行基本检查。
  • Level 2“高级防御”。包含 Level 1 的所有检查,并增加了更严格的格式化字符串检查(如禁止 %n 写入非只读段)。

第二部分:漏洞深度挖掘

1. 静态分析 (IDA Pro)

我们将程序拖入 64 位 IDA 进行分析。

image-20260125234244982

int __cdecl main(int argc, const char **argv, const char **envp)
{
  // ... 省略变量定义 ...
  char buf2[11]; 
  char buf1[11];

  // ... 权限设置与 Logo 打印 ...

  // [漏洞点 1]:直接使用 argv[1] 赋值,且使用了危险函数 strcpy
  v4 = argv[1];
  *(_QWORD *)buf1 = *(_QWORD *)v4;
  strcpy(buf2, "CTFshowPWN");
  printf("%s%s\n", buf1, buf2);

  // [漏洞点 2]:根据 argv[3] 的长度进行 memcpy
  v5 = strtol(argv[3], 0LL, 10);
  memcpy(buf1, argv[2], v5);
  strcpy(buf2, argv[1]);

  printf("%s%s\n", buf1, buf2);
  
  // ...
  
  // [关键逻辑]:后门函数入口
  if ( argc > 4 )
  {
    ((void (__fastcall *)(const char *))Undefined)(argv[4]);
  }
  return 0;
}

2. 代码逻辑解析

  1. 缓冲区溢出风险

    • buf1buf2 只有 11 字节
    • 代码中大量使用了 strcpymemcpy 将命令行参数 argv 复制到这两个缓冲区。
    • 由于 FORTIFY_SOURCE=0,即使我们输入的参数长度远超 11 字节,程序也不会触发 *** buffer overflow detected *** 的报错,而是会发生溢出。
    • 但是,本题并非典型的栈溢出利用(因为没有 ROP 链的空间,也没有 system 函数地址泄露的必要)。
  2. 寻找致命后门

    • main 函数的末尾,我们发现了一个非常显眼的判断:

      if ( argc > 4 )
      {
        ((void (__fastcall *)(const char *))Undefined)(argv[4]);
      }
      
    • Undefined 函数是什么? 跟进该函数,发现其逻辑是打开 /ctfshow_flag 并打印。

    • 触发条件argc > 4。即运行程序时,提供的参数个数必须大于 4 个(包括程序名本身,我们需要提供至少 4 个参数)。

第三部分:解题思路与 Payload

由于保护等级为 Level 0,程序不会拦截我们的超长输入(虽然会溢出覆盖栈上的变量,但只要不覆盖到关键的返回地址导致 Crash 即可)。

本题甚至不需要编写 Python 脚本与服务器交互(题目环境提供了 SSH),我们直接在 Linux 终端通过命令行参数即可解题。

1. Payload 构造逻辑

我们需要满足 argc > 4

  • argv[0]: ./pwn (程序本身)
  • argv[1]: 任意字符 (用于触发前面的逻辑)
  • argv[2]: 任意字符
  • argv[3]: 数字 (用于 strtol,随便写个 0 或 1)
  • argv[4]: 任意字符 (作为参数传入 Undefined,虽然它可能不使用)

Payload: ./pwn a b c d

第四部分:实战操作

# SSH 连接到题目环境后

# 尝试运行,给予足够的参数
ctfshow@pwn:~$ ./pwn aaaaaa bbbbb ccccc ddddd

# 程序输出
CTFshow
...
* Hint: FORTIFY 0 1 2
...
The source code of these three programs is the same...
Here is your flag:
ctfshow{xxxxxxxxxx}

image-20260125234855148

为何这题这么简单?

你可能会问,这题完全没用到溢出啊?

是的,pwn 032 的目的是为了作为一个对照组。 它展示了在没有 FORTIFY 保护的情况下,即使代码写得很烂(到处都是溢出),程序依然会尝试“带病运行”,直到逻辑走到后门函数。

而在接下来的 pwn 033 (Level 1)pwn 034 (Level 2) 中,同样的源码、同样的 Payload,将会因为触发了 __strcpy_chk__printf_chk 的安全检查而直接被系统 Kill 掉。到时候,我们就不能无脑填充参数,而需要精细计算长度绕过检查,或者利用格式化字符串漏洞了。

总结:FORTIFY_SOURCE Level 0 的启示

场景 行为 结果
正常输入 参数长度 < 缓冲区 程序正常运行
恶意输入 (Level 0) 参数长度 > 缓冲区 发生溢出,但程序继续执行 (本题解法)
恶意输入 (Level 1+) 参数长度 > 缓冲区 立即终止,抛出 Stack Smashing Detect

核心思路:在审计代码时,如果发现 FORTIFY_SOURCE 未开启,那么所有未限制长度的字符串操作(strcpy, sprintf)都是潜在的溢出点;且逻辑后门(如本题的 argc 判断)往往是最高效的利用路径。

宇宙级免责声明 🚨 重要声明:本文仅供合法授权下的安全研究与教育目的!

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

🔐 安全研究的正确姿势:

✅ 先授权,再测试

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

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

✅ 尊重隐私,不越界

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

Logo

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

更多推荐