SSP Leak < libc2.27
什么是 SSP 漏洞?
_**Stack Smashing Protector (SSP) **_是一个著名的缓冲区溢出漏洞缓解措施。当一个函数检测栈上的 Canary 被破坏的时候,就会转到 __stack_chk_fial()函数终止程序运行并抛出错误信息。改错误信息包含了 argv[0] 指向的字符串,如果我们能够控制 argv[0],那么将可能造成信息泄露,这一技术称之为 SSP Leak。
先举个简单的例子:
默认情况下 argv[0] 是指向程序路径及名称的指针,当检查到栈溢出时,在标准错误中打印出了这个字符串:
1 |
|
1 | ┌──(pwn_env)─(root㉿kali)-[/home/kali/Desktop] |
接下来加上注释不分的代码,手动修改 argv[0],然后再次编译运行。
可以看到,标准错误中打印出了字符串 “Hello World!”
1 | ┌──(pwn_env)─(root㉿kali)-[/home/kali/Desktop] |
当存在 Canary 保护,错误溢出时程序先从栈 rbp-0x30 上取出 Canary,将其与初始 Canary(fs:0x28)相比较,如果两者不相同,说明发生了错误栈溢出。此时程序不能正常返回,而是调用 __stack_chk_fail()函数,打印出 argv[0] 指向的字符串。
栈是从高地址向低地址增长的,而缓冲区却是从低地址到高地址增长的,所以 argv 数组位于栈中高地址的位置,只要读入字符串足够长,就可以覆盖到 argv[0] 处:
1 | 0x400634 <main+62> mov edi, 0x4006f1 |
所以栈顶到 argv[0] 距离 buf 有 264 个字节,修改我们的 payload:
1 | $ python -c 'print "A"*264' | ./a.out |
libc2.23 :\
Ubuntu 16.04 使用的是 libc-2.23,__stack_chk_fail()函数调用 __fortify_fail()函数,再由 __fortify_fail()调用 __libc_message()函数,将标准错误和 argv[0] 打印出来。
libc2.25 :\
libc-2.25 启用了一个新的函数 __fortify_fail_about(),试图对该泄露问题进行修复。函数的第一个参数为 false 时,将不再进行栈回溯,而是直接打印出字符串”
达成 SSP Leak 的苛刻条件
要成功利用这个漏洞,题目通常需要满足以下几个条件:
- 溢出长度必须足够大: 你的溢出数据必须能跨越茫茫栈空间,精准摸到 argv[0] 的位置。通常需要几百个字节。如果溢出长度只有几十个字节(Off-by-one 或只能刚好覆盖返回地址),就无法使用此漏洞。
- 必须能看到程序的报错输出: 程序崩溃时的报错信息默认输出到 stderr。如果题目环境把 stderr 重定向到了 /dev/null(丢弃报错),或者切断了报错回显,攻击也会失效。
“””
什么是 stderr(标准错误)?
在 Linux 环境下,当一个程序跑起来(变成进程)时,操作系统默认会给它分配三条“管道”:
- stdin (标准输入,代号 0): 程序用来读取你输入数据的管道(比如 gets、scanf 用的就是这条)。
- stdout (标准输出,代号 1): 程序用来打印正常结果的管道(比如 printf(“Hello”) 用的就是这条)。
- stderr (标准错误,代号 2): 程序专门用来打印警告、崩溃、报错信息的管道。
- 如果出题人不想让你利用 SSP Leak 这个漏洞,他会在启动程序的脚本里加上这样一段魔法指令:
1 | ./GUESS 2> /dev/null |
- 这里的
2>意思就是:把代号为 2 的管道(即stderr报错信息)重定向。 - 连起来的意思是:程序正常运行的输出(
stdout)正常返回给玩家,但如果程序发生了崩溃或报错,把报错信息全部倒进黑洞(/dev/null)里。
1 | 你看到了报错字符串(stderr 没关) |
为什么攻击会失效?
假设你现在写了一个极其完美的 SSP Leak 攻击脚本,精确计算了偏移,把 argv[0] 成功指向了内存里的真实 Flag。
如果服务器没有重定向 stderr:
- 程序崩溃。
__stack_chk_fail拿着你的假指针去读取 Flag。- 它把带有 Flag 的报错信息发到
stderr。 stderr顺着网线传回你的电脑,你在屏幕上看到了 Flag,攻击成功!
如果服务器把 stderr 重定向到了 /dev/null:
- 程序崩溃。
__stack_chk_fail拿着你的假指针去读取 Flag。- 它把带有 Flag 的报错信息发到
stderr。 - 服务器直接把这句带有 Flag 的报错信息扔进了垃圾桶(
/dev/null)。 - 你的网络连接瞬间断开,屏幕上什么报错都没看到(也就是所谓的“盲”状态)。
- 虽然你成功控制了程序的执行逻辑,但你看不到输出结果,攻击宣告失败。
“””
- 老版本的靶机环境: 必须是较老的 glibc 版本(如 2.23 或 2.27 的早期版本)。
漏洞的谢幕 (Patch)
由于这个漏洞在过去的 CTF 和真实网络攻击中被滥用,glibc 官方最终痛下杀手。
在后来的更新中(大约从 glibc 2.27 后期和 2.30 开始),开发者直接删除了 __stack_chk_fail 中打印 argv[0] 的代码逻辑。现在的系统如果触发 Canary 报警,只会冷冰冰地输出: *** stack smashing detected ***: terminated 不再附带任何指针解引用的操作,SSP Leak 漏洞从此在现代系统中彻底绝迹。
更新: 2026-03-29 17:57:24
原文: https://www.yuque.com/idcm/wnemg9/rx2t9alqyfw719g5