c++异常处理绕过canary
基础知识
感谢这位师傅的博客,然我茅塞顿开!!
原理
c++中当某个函数throw某个exception时,程序便会从当前函数开始向上回溯调用链,直到找到匹配的catch,执行完catch后,便会接着在catch所在函数继续执行。
结论
异常处理本身存在一些问题,并且使得canary这样的栈保护机制无效。因为异常抛出后的代码不会执行,自然也不会检测canary,自然也不会调用stack_check_fail(). 在此基础上我们发现了一些控制程序流的方式:
- 通过覆盖rbp,进而控制程序流走向。当然前提是栈帧的确使用rbp存储,因为一些情况下程序只依靠rsp增减。
- 通过覆盖ret地址,使异常被另外一个
handler处理 - 在某些情况下还可以通过伪造覆盖类虚表的手法,使其在
cleanup handler执行析构函数的时候劫持程序流(本文不做详细分析)
覆盖ret地址后,就会去执行ret地址的handler函数。前提是这个handler函数要能够匹配这样的异常。
例题
❯ checksec pwn |
这是一个c++程序。

漏洞分析和利用思路
在trace中存在栈溢出,byte_404020数组长度为0x80,但是可以写0x90数据。

溢出后可以将src中字符串覆盖。

在warn中,如果我们想buf中输入数据超过0x10字节,就会触发c++的异常处理函数,并且将src作为参数rdi。
并且buf处还存在栈溢出,我们可以控制rbp和 ret的值,也就可以使用上面提到的c++异常处理机制绕过canary。因为在__cxa_throw执行异常函数后,后面的代码就不会执行了。

在ida右侧的动态链接函数处我们发现有system,交叉引用定位到调用位置,这其实也是一个handler异常处理函数,并且刚好能匹配异常,因为我们的异常传来的参数也是char const*:

所以我们控制传来的参数为/bin/sh就行,也就是在trace中溢出覆盖src为/bin/sh.
在warn中我们控制rbp为可写地址,这里选在data1=0x404100.
exp
from pwn import * |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ya0rk の Blog!




