学习 StackSmash
StackSmash
前言:
StackSmash是一个在 ubuntu16 版本以下的漏洞 ,高版本的ubuntu 已不存在此漏洞。
在libc2.23版本之后就不可利用了,所以简单了解即可
StackSmash,顾名思义,就是要破环栈堆,但有什么用呢?
其实 StackSmash就是利用 Canary保护 的报错机制,如果在栈溢出时修改了 Canary,程序就会抛出错误,执行 __stack_chk_fail 函数来打印出 argv[0] 指针所指向的字符串,即 报错信息
StackSmash 这个漏洞无法让我们 getshell,只能通过 用目标信息覆盖 argv[0] ,来帮助我们 打印出想要的信息。
正常而言
当Canary被修改后,函数返回时,检测到Canary的值错误,会调用 _stack_chk_fail() 函数,而这个函数会打印输入的文件名,即argv[0],存在栈上。
1 | void __attribute__ ((noreturn)) __stack_chk_fail (void) |
stack_chk_fail ()函数中调用了 fortify_fail 函数,并传入 msg:stack smashing detected
之后对msg在 libc_message() 函数中输出,这个函数还把 libc_argv[0] 作为参数输出了。这个参数其实就是 argv[0] ,也就是 程序的名称
实际效果是这样的(这是在 ubuntu16 的虚拟机中演示的)

例题:
[2021 鹤城杯]easyecho
64位小端序程序保护全开

代码分析:
程序会 循环打印 用户输入,如果用户输入 “backdoor” ,就会调用 v9 ,一直到 用户输入 “exitexit”才退出循环

将 v9 等于 sub_CF0(),后续调用 v9相当于 调用 sub_CF0()
1 | v9 = sub_CF0; |
sub_CF0(),打开了 flag文件,并将 其读到 .bss段上


sub_E40(v8) ,读入 16个字节 (之前定义了char v8[16];)

代码分析完成了
用 gdb 调一遍看看会有什么惊喜

在sub_E40(v8),输完16个字节后发现栈上有一个 程序地址 可以帮助我们拿到程序基址,绕过PIE保护
这个地址就是 sub_E40(v8) 的调用地址 ,main()函数调用sub_E40(v8)完了,就将它的地址压栈了

argv[0]位于栈上,地址是 0x7fffe5ff3fd8,输入 地址是从 rdi的位置,也就是 0x7fffe5ff3e70开始的,offest = 0x168


解题思路:
输入 “backdoor” ,程序会调用sub_CF0(),将 flag的内容读到
.bss段上通过 sub_E40()输入v8,printf()在打印 v8时 ,会将 sub_E40(v8) 的调用地址一起打印,我们拿到了程序基址 => 拿到了 flag在
.bss段上的地址
1 | sub_E40(v8); |
- 将 flag的地址 覆盖 argv[0],触发 Canary保护 的报错,将 flag的内容 作为报错信息 打印出来
最后要输入 “exitexit”结束后 程序才会检查 Canary是否被修改
EXP
1 | from pwn import * |
效果:

总结:
简单的 StackSmash就是 拿两个地址 ,一个是 flag的地址、一个是 argv[0]的地址,然后用 flag地址 覆盖栈上 argv[0]的地址,然后修改 Canary 的值,触发 Canary保护的报错,打印 flag。
另外注意 ,本地调试时如果不是在 ubuntu16上的话 ,argv[0]到我们输入的地址之间的 offest是不一样的,作者刚开始在 ubuntu22.04上做这道题时,用 gdb 调出来的偏移是 0x198,而远端是ubuntu16,是打不通的。






