核心内容摘要
小白也能学会!AnythingtoRealCharacters2511让动漫头像变真实照片
本文仅用于技术研究禁止用于非法用途。
Author:枷锁在经历了FORTIFY_SOURCE系列pwn
的编译时检查防御后PWN 035带我们回归到了经典的栈溢出 (Stack Overflow)领域。
但这道题与常规的“溢出 - 覆盖返回地址 - ROP/Shellcode”完全不同。
在这里我们不需要精心构造跳转地址甚至不需要控制程序流向哪里。
我们的目标只有一个让程序崩溃。
因为有些时候程序的“临终遗言”比它活着的时候更有价值。
这道题完美诠释了什么是“置之死地而后生”。
pwn 035 此时无声胜有声利用崩溃获取 Flag题目信息与环境侦察题目描述pwn35: 正式开始栈溢出了先来一个最最最最简单的吧 用户名为 ctfshow 密码 为 123456 请使用 ssh软件连接 ssh ctfshow题目地址 -p题目端口号 不是nc连接解题过程首先使用checksec检查程序保护情况。
Arch: i
-little (32位)RELRO:Partial RELRO(GOT表前段只读PLT可写)Stack:No canary found(无栈哨兵这是栈溢出的关键前提)NX:Enabled(数据段不可执行这意味着我们不能直接在栈上运行 Shellcode)PIE:No PIE(地址固定方便定位虽然本题不需要定位)侦察分析 虽然开启了 NX 保护无法直接执行 Shellcode但No canary意味着我们可以随意覆盖栈上的数据包括 EBP 和返回地址Return Address。
分机制详解 —— 信号处理 (Signal Handler)在 Linux 系统中当程序发生严重异常如除以零、非法内存访问时内核会向进程发送一个信号 (Signal)。
什么是 SIGSEGV信号名称SIGSEGV (Signal
中文含义段错误 (Segmentation Fault)触发条件当程序试图访问未分配的内存或者试图向只读内存如.rodata写入数据时触发。
常见场景在栈溢出攻击中如果我们覆盖了返回地址为一个无效地址如0x41414141当函数尝试ret跳转时CPU 就会因为无法访问该地址而抛出异常进而触发 SIGSEGV。
信号处理函数 (Signal Handler)程序员可以通过signal()系统调用自定义当某个信号发生时要执行的代码。
默认行为如果你不设置发生 SIGSEGV 时程序会直接终止并 core dump。
自定义行为如果程序注册了signal(11, handler_func)那么当程序崩溃时它不会立即死亡而是会先去执行handler_func函数。
这给了攻击者可乘之机。
分代码审计与漏洞挖掘
静态分析 (IDA Pro)将程序拖入 IDA 32位我们重点关注main函数的初始化流程。
int __cdecl main(int argc, const char **argv, const char **envp) { FILE *stream; // [esp0h] [ebp-1Ch] // [步骤 1]尝试打开 Flag 文件 // r 表示以只读模式打开 /ctfshow_flag stream fopen(/ctfshow_flag, r); if ( !stream ) { // 如果打开失败比如本地没有这个文件程序会提示并退出 puts(/ctfshow_flag: No such file or directory.); exit(
; } // [步骤 2]将 Flag 读入全局内存 // fgets 从文件中读取 64 字节存放到全局变量 flag 中 // 注意这个 flag 变量位于 .bss 段或 .data 段在整个程序生命周期内都存在 fgets(flag, 64, stream); // [步骤 3]设置“崩溃触发器” (关键点!) // signal(11, ...) 注册了 SIGSEGV 信号的处理函数 // 意思是如果程序之后发生了段错误崩溃先别急着死去执行 sigsegv_handler 函数 signal(11, (__sighandler_t)sigsegv_handler); // ... 打印一堆 Logo 信息无关紧要 ... puts(asc_
; // ... puts(Where is flag?\n); // [步骤 4]参数检查 if ( argc 1 ) { puts(Try again!); } else { // [步骤 5]漏洞触发点 // 将用户输入的第一个参数 argv[1] 传入 ctfshow 函数 // 这里就是我们注入 Payload 的地方 ctfshow((char *)argv[1]); // 如果 ctfshow 正常返回没崩溃会执行这行 printf(QaQ!FLAG IS NOT HERE! Here is your input : %s, argv[1]); } return 0; }详细分析Flag 在哪里程序一启动fgets就把 flag 的内容读到了内存里全局变量flag。
这就像是把宝藏从保险箱文件系统拿出来放到了桌子上内存。
机关是什么signal(11, sigsegv_handler)设置了一个机关。
它告诉操作系统“如果有人把桌子掀了触发崩溃请自动执行后续的sigsegv_handler操作。
”如何触发ctfshow函数接收了我们的输入。
如果我们在那里制造溢出程序就会崩溃从而触发上面的机关。
深入信号处理函数跟进sigsegv_handler函数在 IDA 中双击handler地址void __noreturn sigsegv_handler() { // 当程序崩溃时这里会执行 // 它将之前读取的全局变量 flag 输出到标准错误流 (stderr) fprintf(stderr, %s\n, flag); fflush(stderr); exit(
; }分析这个函数就是我们梦寐以求的终点。
只要程序崩溃它就会通过fprintf把 Flag 打印到标准错误输出并刷新缓冲区确保我们能看到。
寻找溢出点跟进ctfshow函数char *__cdecl ctfshow(char *src) { // [espCh] [ebp-6Ch] BYREF // 虽然数组定义为 104 字节但栈偏移显示它距离 EBP 有 0x6C (108字节) char dest[104]; // [致命漏洞]strcpy 无长度限制 // strcpy 会将 src 的内容复制到 dest直到遇到 \0 // 我们可以通过 argv[1] 传入超长字符串覆盖 dest进而覆盖 EBP 和 返回地址 return strcpy(dest, src); }精确栈布局分析IDA 注释[ebp-6Ch]揭示了关键的内存布局信息dest 缓冲区起始地址ebp - 0x6C(即ebp - 108字节)。
Saved EBP位于ebp(占用 4 字节)。
Return Address位于ebp 4。
计算崩溃阈值 要覆盖到返回地址我们需要填充的数据长度 108(填充缓冲区及对齐填充) 4(覆盖 Old EBP) 112 字节。
只要我们输入的字符串长度超过112字节第
字节就会覆盖返回地址。
一旦返回地址被修改为乱码如0x61616161函数返回时就会触发段错误。
分解题思路这道题的逻辑闭环非常完美它是一个典型的“诱捕”结构但也留下了后门Flag 已在内存中程序启动时Flag 就已经被读入到全局变量里了。
崩溃即胜利程序告诉操作系统“如果我崩了SIGSEGV请把 Flag 打印出来再死”。
制造崩溃ctfshow函数中存在strcpy栈溢出漏洞。
我们只需要输入足够长的字符串覆盖掉函数的返回地址Return Address。
触发流程我们输入 200 个 ‘A’远超 112 字节。
strcpy将其写入栈。
栈上的Old EBP被覆盖。
栈上的Return Address被覆盖为0x41414141(‘AAAA’)。
ctfshow函数执行完毕执行ret指令。
CPU 尝试跳转到0x41414141。
0x41414141是非法内存地址 -触发 SIGSEGV。
内核捕获信号跳转执行sigsegv_handler。
fprintf(stderr, ...)执行Flag 到手。
攻击策略“只要输入得够长程序就不知道该去哪然后它就会把 Flag 给我。
”我们甚至不需要计算精确的偏移量不需要构造 ROP 链不需要知道system地址只需要由着性子输入一大串A即可。
分实战操作由于是 SSH 连接我们不需要编写 Python 脚本直接在终端操作即可。
尝试正常输入./pwn hello # 程序正常运行打印输入内容没有 Flag QaQ! FLAG IS NOT HERE! Here is your input: hello
尝试攻击 (Payload)构造一个明显超过栈空间的字符串大于 112 字节。
为了保险我们直接输入一大串比如 200 个字符。
# 在终端直接运行参数输入一长串 a ./pwn aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
运行结果CTFshow ... * Type : Stack_Overflow * Hint : See what the program does! ... Where is flag? ctfshow{xxxxxxxxxxxxxxxxxxx}程序在尝试返回时崩溃触发了信号处理函数成功吐出了 Flag。
总结PWN 035 的核心逻辑步骤动作本质预设程序读取 Flag 并注册SIGSEGV句柄“临终遗言”机制崩了就打印 Flag。
漏洞strcpy无限制复制制造混乱允许破坏栈结构。
触发输入超长字符串覆盖返回地址实施谋杀让程序跳向非法地址导致 Crash。
结果信号处理函数接管控制权获取遗产程序崩溃前打印 Flag。
核心启示 在 PWN 中Crash崩溃并不总是坏事。
有时候崩溃能泄露信息如报错信息中包含内存地址这是 Stack Smash 保护的利用点之一。
有时候崩溃本身就是程序逻辑的一部分如本题的信号处理。
在分析程序时一定要留意signal、sigaction等注册异常处理的函数它们往往藏着作者留下的“后门”或“彩蛋”。
宇宙级免责声明 重要声明本文仅供合法授权下的安全研究与教育目的
合法授权本文所述技术仅适用于已获得明确书面授权的目标或自己的靶场内系统。
未经授权的渗透测试、漏洞扫描或暴力破解行为均属违法可能导致法律后果包括但不限于刑事指控、民事诉讼及巨额赔偿。
道德约束黑客精神的核心是建设而非破坏。
请确保你的行为符合道德规范仅用于提升系统安全性而非恶意入侵、数据窃取或服务干扰。
风险自担使用本文所述工具和技术时你需自行承担所有风险。
作者及发布平台不对任何滥用、误用或由此引发的法律问题负责。
合规性确保你的测试符合当地及国际法律法规如《计算机欺诈与滥用法案》CFAA、《通用数据保护条例》GDPR等。
必要时咨询法律顾问。
最小影响原则测试过程中应避免对目标系统造成破坏或服务中断。
建议在非生产环境或沙箱环境中进行演练。
数据保护不得访问、存储或泄露任何未授权的用户数据。
如意外获取敏感信息应立即报告相关方并删除。
免责范围作者、平台及关联方明确拒绝承担因读者行为导致的任何直接、间接、附带或惩罚性损害责任。
安全研究的正确姿势✅ 先授权再测试✅ 只针对自己拥有或有权测试的系统✅ 发现漏洞后及时报告并协助修复✅ 尊重隐私不越界⚠️ 警告技术无善恶人心有黑白。
请明智选择你的道路。