PWN之canary骚操作
最近做题遇到一些canary的保护的题目,于是想着搜集整理一波有关绕过canary的操作。
Canary保护机制的原理,是在一个函数入口处从fs段内获取一个随机值,一般存到EBP - 0x4(32位)或RBP - 0x8(64位)的位置。如果攻击者利用栈溢出修改到了这个值,导致该值与存入的值不一致,__stack_chk_fail函数将抛出异常并退出程序。Canary最高字节一般是\x00,防止由于其他漏洞产生的Canary泄露
需要注意的是:canary一般最高位是\x00,64位程序的canary大小是8个字节,32位的是4个字节,canary的位置不一定就是与ebp存储的位置相邻,具体得看程序的汇编操作
0x01
泄漏canary:
这个操作大概分两种:
-
1.先通过覆盖Canary最后一个”\x00″字节,来防止0截断,进而通过printf等函数打印出4/8位的Canary,又或者通过格式化字符串泄漏输出canary,之后,计算好偏移,将Canary填入到相应的溢出位置,实现为所欲为的栈溢出
-
SSP leak,这个是反向思维的操作,通过Canary的报错输出来泄露内存,如下面源代码所示。libcargv[0]应用程序的参数,在Canary出错报错输出中会打印出应用程序的路径,这正是__libc_argv[0]的内容,如果栈溢出到能够覆盖__libc_argv[0],那么Canary就会报错打印相应的指针内容
"debug/fortify_fail.c"
void
__attribute__ ((noreturn))
__fortify_fail (msg)
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] ?: "<unknown>");
}
libc_hidden_def (__fortify_fail)
0x02
修改canary
由于多线程中Canary存入TLS结构体,而TLS位于多线程内部栈的高地址,并且该结构体与当前栈差距不足一个page,导致我们能对其进行修改,改为我们想要的值,从而绕过检测。
这种操作一般在多线程下进行,而且溢出的字节必须足够大才能溢出到tls的位置从而修改canary
题目详见:https://www.jianshu.com/p/110f715c210f中的babystack
0x03
爆破canary
Canary在设计中存在一个缺陷,这个缺陷或许不是由于Canary的设计问题,而是linux的机制导致的,那就是子进程会继承父进程的Canary,每一次fork的进程内存布局与父进程一致,Canary值也相同
这样当我们子进程由于Canary判断不正确导致程序crash后,父进程不会Crash
我们就完全可以利用这样的特点,彻底逐个字节将Canary爆破出来,爆破模板如下
print "[+] Brute forcing stack canary "
start = len(p)
stop = len(p)+8
while len(p) < stop:
for i in xrange(0,256):
res = send2server(p + chr(i))
if res != "":
p = p + chr(i)
#print "\t[+] Byte found 0x%02x" % i
break
if i == 255:
print "[-] Exploit failed"
sys.exit(-1)
canary = p[stop:start-1:-1].encode("hex")
print " [+] SSP value is 0x%s" % canary
0x04
绕过canary
这种操作的核心思想就是想办法让他不执行canary的报错或者直接跳过canary的检查
-
1.利用格式化字符串或者数组下标越界,可以栈地址任意读写,不必连续向栈上写,直接写ebp和ret因此不会触发Canary检查。
-
2.Hijack __stack_chk_fail
当Canary验证失败的时候是进入到stack_chk_failed函数中,它在该函数中完成报错输出,但是如果我们能够劫持该函数,让它不在完成该功能,那么Canary就形同虚设,我们就可以为所欲为栈溢出了
但需要注意的是:种技术并不是我们一般方式的Hijack GOT表,一般我们HijackGOT是GOT表绑定了真实地址之后,我们修改它,让程序执行其他的函数。 Got表中要绑定真实地址必须是得执行过一次,然而stack_chk_failed执行第一次的时候程序就报错退出了,因此我们需要Overwrite的尚未执行过的stack_chk_failed的GOT表项,此时GOT表中应该存贮这stack_chk_failed PLT[1]的地址
- 通过一些其他的机制跳过canary的检查,比如Shanghai-DCTF-2017 线下攻防Pwn题 中:–利用c++异常机制绕过canary检查
参考链接:
http://tacxingxing.com/2017/07/13/canary/
https://veritas501.space/2017/04/28/%E8%AE%BAcanary%E7%9A%84%E5%87%A0%E7%A7%8D%E7%8E%A9%E6%B3%95/
https://bestwing.me/2017-Shanghai-DCTF-final-pwn.html#%E6%80%9D%E8%B7%AF
https://github.com/solei1/solei1.github.io/wiki/Canary-bypass