绕过canary & 确定基址
Canary
概念
canary是一个保护机制,具体的实现,是在ebp和esp之间放入一个canary值,在程序结束的时候判断是否被更改,改变则通过__stack_chk_fail结束当前进程。一定程度上可以防止栈溢出攻击。
注意这里的var_8,就是放canary值的偏移。
特性
占用一个字的大小,canary值的最低位一定是\x00。
绕过方法
劫持__stack_chk_fail
canary被改写后,程序流程就会经过__stack_chk_fail退出程序。如果将这个函数劫持
- 例如更改got表
就可以在canary检查失败后劫持程序。
泄露canary值
canary保护机制仅仅检查canary的值是否被篡改,而不会检查栈的其他内容。因此如果每次栈溢出的时候覆盖为相同的值就可以通过检查。
如何泄露
canary在存储时的最高位(小端序)是’\x00’,会截断canary值和前面的内容。只要将’\x00’覆盖成不会截断字符串打印的值,就可以通过打印字符串的形式得到当前canary值。
canary的生成
同一个线程里面的canary值是相同的。
爆破
还没试过。与fork()
这个函数和线程有关。
覆盖与栈中canary值进行比较的值
思路
漏洞点
程序在判断大小的时候nbytes是做的有符号数,而在read中做参数的时候是无符号数。输入负数即可绕过判断进行栈溢出。
流程
这个二进制文件checksec一下会发现所有的保护都开了。
FULL RELRO
意味着没法改got表,所以要泄露canary的值。
要进行三次栈溢出。
- 打印canary的值& 泄露程序基址
- 泄露libc基址
- getshell
程序基址怎么泄露
我暂时还没有找到相关资料,但是我打开了一些二进制文件,发现在main函数里面的时候:
- 32位的ebp指向的栈里保存的是0x0
- 64位的rbp指向的栈里保存的是__libc_csu_init的地址。
- 我还搞不清楚的是为什么__libc_csu_init的地址不是7f开头而是与程序加载基址有关。
- 2019/7/20 补充:这是一个程序自带的函数。不是动态链接库里面的。
我们如果用这个想法来看这个程序的rbp,
虽然他去符号化了,但是按照这个思路来说,就可以根据__libc_csu_init的地址找到main的地址。
1 |
|
0x9cc
是从ida里面看的main地址。因为他开了pie,所以这个只是一个偏移地址。程序加载基址是__libc_csu_init_addr & (~0xfff)
待补充原因
libc基址怎么泄露
使用puts打印出puts@got的内容。
getshell
可以使用one_gadget,也可以直接构造system('/bin/sh')
,但是如果打远程的话需要知道libc的版本。
系统中断不行,找不到int 0x80。
注意点
- 程序是64位,采用寄存器传参,需要使用pop_rdi_ret。
- 地址低位为
\x00
时在u64()
的前面补\x00
,高位则在后面。
坑
为什么gdb.attach的位置会影响exp???
exp
1 |
|