The Way to Bypass Canary

碎碎念,表示之前确实没改过TLS结构(线程局部存储)

多线程->SSP LEAK

例网鼎杯第一场的guess:

1
https://kirin-say.top/2018/08/21/%E7%BD%91%E9%BC%8E%E6%9D%AF%E7%AC%AC%E4%B8%80%E5%9C%BA-Re-%E9%83%A8%E5%88%86Pwn/#0x04-guess

主要用于环境是当前子线程崩溃,但是主线程不断创建新线程
且各线程之间环境(加载地址,canary……)相同,所以此时可以多次利用漏洞:
当触发___stack_chk_fail时:会输出:

1
*** stack smashing detected ***: (string *)argv[0] terminated

通常情况下argv[0]指向调用程序时的名称
此时如果将其覆盖为其他地址便可以leak程序信息
根据环境不同调整方法即可
例如此利用链:

1
2
3
4
5
6
7
#假设argv[0]附近存在libc内的地址
覆盖argv[0]低字节指向此处(需要爆破1-2 bytes,类似利用unsorted bin的fd中的main arena写IO_FILE)
触发___stack_chk_fail来leak libc
通过libc addr向argv[0]写入environ addr->leak stack
而后利用栈中信息leak canary
canary+fake_bp+system("/bin/sh")
get shell

爆破

因为各线程之间canary相同
有时候可以根据程序特点选择爆破canary

故意触发Canary

只要在___stack_chk_fail中可以劫持程序流即可
例如:

1
https://kirin-say.top/2019/02/08/pwnable-tw-3x17/#%E6%96%B9%E6%A1%88%E4%B8%89

Modify the TLS

正常情况下,canary取值是:
32 bits:

1
2
mov     eax, large gs:14h
mov [ebp+var_C], eax

64 bits:

1
2
mov     rax, fs:28h
mov [rbp+var_8], rax

而段寄存器fs && gs的定义是指向本线程的TLS结构
任意一个开启canary保护的程序(32 bits&&64 bits结果相同)中调试:
首先我们先查看此时canary的值:

1
2
pwndbg> x/xg $rbp-0x8
0x7fffffffda98: 0xf2bd153e3343b900

确定此时TLS结构的位置

1
2
3
pwndbg> tls
$1 = 0x0
tls : 0x7ffff7fd7700

查看此结构位置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x400000 0x401000 r-xp 1000 0 /home/regedit/桌面/pwnable/canary_test
0x600000 0x601000 r--p 1000 0 /home/regedit/桌面/pwnable/canary_test
0x601000 0x602000 rw-p 1000 1000 /home/regedit/桌面/pwnable/canary_test
0x7ffff7a0d000 0x7ffff7bcd000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7bcd000 0x7ffff7dcd000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7dcd000 0x7ffff7dd1000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7dd1000 0x7ffff7dd3000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7dd3000 0x7ffff7dd7000 rw-p 4000 0
0x7ffff7dd7000 0x7ffff7dfd000 r-xp 26000 0 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ffff7fd6000 0x7ffff7fd9000 rw-p 3000 0
0x7ffff7ff7000 0x7ffff7ffa000 r--p 3000 0 [vvar]
0x7ffff7ffa000 0x7ffff7ffc000 r-xp 2000 0 [vdso]
0x7ffff7ffc000 0x7ffff7ffd000 r--p 1000 25000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ffff7ffd000 0x7ffff7ffe000 rw-p 1000 26000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ffff7ffe000 0x7ffff7fff000 rw-p 1000 0
0x7ffffffdd000 0x7ffffffff000 rw-p 22000 0 [stack]
0xffffffffff600000 0xffffffffff601000 r-xp 1000 0 [vsyscall]

可以看到其在vvar与 /lib/x86_64-linux-gnu/ld-2.23.so之间的一段空间
且:具有可写权限
查看此结构数据:

1
2
3
4
5
6
pwndbg> x/10xg 0x7ffff7fd7700
0x7ffff7fd7700: 0x00007ffff7fd7700 0x00007ffff7fd6010
0x7ffff7fd7710: 0x00007ffff7fd7700 0x0000000000000000
0x7ffff7fd7720: 0x0000000000000000 0xf2bd153e3343b900
0x7ffff7fd7730: 0x54dbde4a271d42e3 0x0000000000000000
0x7ffff7fd7740: 0x0000000000000000 0x0000000000000000

可以看到tls结构偏移0x28的位置正是此程序的canary
(32 bits对应0x14偏移)
所以最终程序返回时对比的canary正是此处的值:

1
2
3
4
mov     rcx, [rbp+var_8]
xor rcx, fs:28h
jz short ......
call ___stack_chk_fail

所以当我们可以覆盖tls结构来绕过canary检测:
Something for Test:
首先为了调试先关闭系统的地址随机化:

1
2
3
4
5
echo "0" > /proc/sys/kernel/randomize_va_space
#重启后会重新设为2
#0 关闭
#1 共享库、栈、mmap、VDSO随机化
#2 1+堆随机

canary_test.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<stdio.h>
#include <unistd.h>
int main(){
char a;
char b;
read(0,&a,0x100);
puts("Modify the canary?[y/n]");
read(0,&b,2);
if(b=='y'){
long canary_addr=0x7ffff7fd7700+0x28;
read(0,canary_addr,16);
}
}
#gcc -o canary_test ./canary_test.c

ida简单查看栈分布:

1
10 bytes(a&b&canary_addr)+canary+rbp

因为采取read,我们输入”a”*17+”\n”进行测试(覆盖canary为”aaaaaaa\n”)
此时对比是否修改tls结构的情况:

test
可以看到修改后程序正常返回
因此可以考虑通过此方法绕过canary保护