CTF Re&&Pwn

The Way to Bypass Canary

2019-02-26  本文已影响55人  Kirin_say

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

多线程->SSP LEAK

例网鼎杯第一场的guess:

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时:会输出:

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

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

#假设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中可以劫持程序流即可
例如:

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:

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

64 bits:

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

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

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

确定此时TLS结构的位置

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

查看此结构位置:

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之间的一段空间
且:具有可写权限
查看此结构数据:

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正是此处的值:

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

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

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

canary_test.c:

#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简单查看栈分布:

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

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

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

猜你喜欢

热点阅读