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!