新聞中心
一、概述

創(chuàng)新互聯(lián)建站是一家專業(yè)提供邢臺(tái)縣企業(yè)網(wǎng)站建設(shè),專注與成都網(wǎng)站建設(shè)、做網(wǎng)站、H5技術(shù)、小程序制作等業(yè)務(wù)。10年已為邢臺(tái)縣眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)站制作公司優(yōu)惠進(jìn)行中。
棧溢出保護(hù)是一種緩沖區(qū)溢出攻擊緩解手段,當(dāng)函數(shù)存在緩沖區(qū)溢出攻擊漏洞時(shí),攻擊者可以覆蓋棧上的返回地址來讓shellcode能夠得到執(zhí)行。當(dāng)啟用棧保護(hù)后,函數(shù)開始執(zhí)行的時(shí)候會(huì)先往棧里插入cookie信息,當(dāng)函數(shù)真正返回的時(shí)候會(huì)驗(yàn)證cookie信息是否合法,如果不合法就停止程序運(yùn)行。攻擊者在覆蓋返回地址的時(shí)候往往也會(huì)將cookie信息給覆蓋掉,導(dǎo)致棧保護(hù)檢查失敗而阻止shellcode的執(zhí)行。在Linux中我們將cookie信息稱為canary(以下統(tǒng)一使用canary)。
gcc在4.2版本中添加了-fstack-protector和-fstack-protector-all編譯參數(shù)以支持棧保護(hù)功能,4.9新增了-fstack-protector-strong編譯參數(shù)讓保護(hù)的范圍更廣。以下是-fstack-protector和-fstack-protector-strong的區(qū)別:
Linux系統(tǒng)中存在著三種類型的棧:
- 應(yīng)用程序棧:工作在Ring3,由應(yīng)用程序來維護(hù);
- 內(nèi)核進(jìn)程上下文棧:工作在Ring0,由內(nèi)核在創(chuàng)建線程的時(shí)候創(chuàng)建;
- 內(nèi)核中斷上下文棧:工作在Ring0,在內(nèi)核初始化的時(shí)候給每個(gè)CPU核心創(chuàng)建一個(gè)。
二、 應(yīng)用程序棧保護(hù)
1. 棧保護(hù)工作原理
下面是一個(gè)包含棧溢出的例子:
- /* test.c */
- #include
- #include
- int main(int argc, char **argv)
- {
- char buf[16];
- scanf("%s", buf);
- printf("%s\n", buf);
- return 0;
- }
我們先禁用棧保護(hù)功能看看執(zhí)行的結(jié)果
- [root@localhost stackp]# gcc -o test test.c -fno-stack-protector
- [root@localhost stackp]# python -c "print 'A'*24" | ./test
- AAAAAAAAAAAAAAAAAAAAAAAA
- Segmentation fault <- RIP腐敗,導(dǎo)致異常
當(dāng)返回地址被覆蓋后產(chǎn)生了一個(gè)段錯(cuò)誤,因?yàn)楝F(xiàn)在的返回地址已經(jīng)無效了,所以現(xiàn)在執(zhí)行的是CPU的異常處理流程。我們打開棧保護(hù)后再看看結(jié)果:
- [root@localhost stackp]# gcc -o test test.c -fstack-protector
- [root@localhost stackp]# python -c "print 'A'*25" | ./test
- AAAAAAAAAAAAAAAAAAAAAAAAA
- *** stack smashing detected ***: ./test terminated
這時(shí)觸發(fā)的就不是段錯(cuò)誤了,而是棧保護(hù)的處理流程,我們反匯編看看做了哪些事情:
- 0000000000400610
: - 400610: 55 push %rbp
- 400611: 48 89 e5 mov %rsp,%rbp
- 400614: 48 83 ec 30 sub $0x30,%rsp
- 400618: 89 7d dc mov %edi,-0x24(%rbp)
- 40061b: 48 89 75 d0 mov %rsi,-0x30(%rbp)
- 40061f: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax <- 插入canary值
- 400626: 00 00
- 400628: 48 89 45 f8 mov %rax,-0x8(%rbp)
- 40062c: 31 c0 xor %eax,%eax
- 40062e: 48 8d 45 e0 lea -0x20(%rbp),%rax
- 400632: 48 89 c6 mov %rax,%rsi
- 400635: bf 00 07 40 00 mov $0x400700,%edi
- 40063a: b8 00 00 00 00 mov $0x0,%eax
- 40063f: e8 cc fe ff ff callq 400510 <__isoc99_scanf@plt>
- 400644: 48 8d 45 e0 lea -0x20(%rbp),%rax
- 400648: 48 89 c7 mov %rax,%rdi
- 40064b: e8 80 fe ff ff callq 4004d0
- 400650: b8 00 00 00 00 mov $0x0,%eax
- 400655: 48 8b 55 f8 mov -0x8(%rbp),%rdx <- 檢查canary值
- 400659: 64 48 33 14 25 28 00 xor %fs:0x28,%rdx
- 400660: 00 00
- 400662: 74 05 je 400669
# 0x400669 - 400664: e8 77 fe ff ff callq 4004e0 <__stack_chk_fail@plt>
- 400669: c9 leaveq
- 40066a: c3 retq
我們看到函數(shù)開頭(地址:0x40061f)處gcc編譯時(shí)在棧幀的返回地址和臨時(shí)變量之間插入了一個(gè)canary值,該值是從%fs:0x28里取的,棧幀的布局如下:
- stack:
- | ...... |
- | orig_return |
- | orig_rbp | <- %rbp
- | canary | <- -0x8(%rpb), 既 %fs:0x28
- | local variables |
- | | <- %rsp
在函數(shù)即將返回時(shí)(地址:0x400655)檢查棧中的值是否和原來的相等,如果不相等就調(diào)用glibc的_stackchk_fail函數(shù),并終止進(jìn)程。
2. canary值的產(chǎn)生
這里以x64平臺(tái)為例,canary是從%fs:0x28偏移位置獲取的,%fs寄存器被glibc定義為存放tls信息的,我們需要查看glibc的源代碼:
- typedef struct
- {
- void *tcb; /* Pointer to the TCB. Not necessarily the
- thread descriptor used by libpthread. */
- dtv_t *dtv;
- void *self; /* Pointer to the thread descriptor. */
- int multiple_threads;
- int gscope_flag;
- uintptr_t sysinfo;
- uintptr_t stack_guard; <- canary值,偏移位置0x28處
- uintptr_t pointer_guard;
- ......
- } tcbhead_t;
結(jié)構(gòu)體tcbheadt就是用來描述tls的也就是%fs寄存器指向的位置,其中+0x28偏移位置的成員變量stackguard就是canary值。另外通過strace ./test看到在進(jìn)程加載的過程中會(huì)調(diào)用arch_prctl系統(tǒng)調(diào)用來設(shè)置%fs的值,
- root@localhost stackp]# strace ./test
- execve("./test", ["./test"], [/* 24 vars */]) = 0
- ......
- arch_prctl(ARCH_SET_FS, 0x7f985a041740) = 0
- ......
產(chǎn)生canary值的代碼在glibc的dlmain和_libcstart_main函數(shù)中:
- /* Set up the stack checker's canary. */
- uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random);
- # ifdef THREAD_SET_STACK_GUARD
- THREAD_SET_STACK_GUARD (stack_chk_guard);
- # else
- __stack_chk_guard = stack_chk_guard;
- # endif
dlrandom是一個(gè)隨機(jī)數(shù),它由dlsysdepstart函數(shù)從內(nèi)核獲取的。dlsetupstackchkguard函數(shù)負(fù)責(zé)生成canary值,THREADSETSTACK_GUARD宏將canary設(shè)置到%fs:0x28位置。
在應(yīng)用程序棧保護(hù)中,進(jìn)程的%fs寄存器是由glibc來管理的,并不涉及到內(nèi)核提供的功能。
3. x32應(yīng)用程序棧保護(hù)
解讀完了x64的實(shí)現(xiàn),我們來看看x32下面的情況,我們還是使用上面例子的代碼在x32的機(jī)器上編譯,得到下面的代碼:
- 08048464
: - ......
- 8048474: 65 a1 14 00 00 00 mov %gs:0x14,%eax # 插入canary值
- 804847a: 89 44 24 3c mov %eax,0x3c(%esp)
- ......
- 80484aa: 65 33 15 14 00 00 00 xor %gs:0x14,%edx # 檢查canary值
- 80484b1: 74 05 je 80484b8
# 0x80484b8 - 80484b3: e8 c0 fe ff ff call 8048378 <__stack_chk_fail@plt>
- 80484b8: c9 leave
- 80484b9: c3 ret
網(wǎng)站題目:解讀Linux安全機(jī)制之棧溢出保護(hù)
網(wǎng)址分享:http://www.dlmjj.cn/article/dpghohj.html


咨詢
建站咨詢
