int main() { asm("\ needle0: jmp there\n\ here: pop %rdi\n\ xor %rax, %rax\n\ movb $0x3b, %al\n\ xor %rsi, %rsi\n\ xor %rdx, %rdx\n\ syscall\n\ there: call here\n\ .string \"/bin/sh\"\n\ needle1: .octa 0xdeadbeef\n\ "); }
咱們先提取要注入的payload,查看機器代碼:
shell
在64位系統上,代碼段一般位於0x400000,在該二進制文件中,咱們的代碼位於0x4b8的偏移量處,並在偏移量0x4d5以前完成,共有29個字節:
編程
查看咱們的shellcode:
ubuntu
#include <stdio.h> int main() { char name[64]; puts("What's your name?"); gets(name); printf("Hello, %s!\n", name); return 0; }
咱們能夠想辦法繞過這些方法,使用gcc的-fno-stack-protector選項禁用堆棧保護,編譯victim,而後使用execstack -s指令禁用可執行文件空間保護,發現提示沒有安裝:
架構
安裝完成以後,禁用可執行文件空間保護,而後在運行文件時禁用ASLR:
函數
咱們再在原victim.c代碼的基礎上加上一句printf("%p\n", name);
,打印緩衝區的位置,而後再編譯運行:
工具
後面的過程當中應該也會出現該緩衝區的地址,咱們讓它以小端法顯示:
佈局
這個時候咱們攻擊咱們的victim.c程序,能夠看到攻擊成功:
操作系統
接着,咱們再經過一個例子來看看打補丁的重要性,在之前,咱們能夠經過查看/proc/pid/stat來讀取任何進程的ESP註冊表,可是這種漏洞很早就被修復了,咱們僞裝目前在沒有打補丁的系統上,先查看全部進程的esp:
3d
咱們先運行禁用了ASLR的victim程序:
指針
再在另外一個終端窗口查看victim程序的esp:
所以,當程序正在等待用戶輸入時,它的堆棧指針0x7fffffece8,咱們計算從這個指針到名稱緩衝區的距離:
如今從新運行啓用了ASLR的victim程序:
咱們查找victim進程的esp指針,而後添加上偏移量,就是上一步中運行victim程序的緩衝區地址:
接着用管道命令來進行演示:
在另外一個終端中輸入以下命令:
能夠看到攻擊成功,獲取到了shell:
先經過運行execstack -c指令從新啓動可執行空間保護,此外,在ROP攻擊中代碼片斷從可執行內存中挑選出來,例如,它們多是libc的片斷,因此咱們還要經過locate指令找到libc的位置,如圖所示,咱們選擇第一個:
咱們但願執行pop %rdi retq
,而指向「/bin/sh」的指針位於堆棧的頂部。這將在推動堆棧指針以前將指針分配給rdi,相應的機器代碼是兩個字節的序列0x5f 0xc3,它應該在libc的某處發生。可是沒有Linux工具可以直接在文件中搜索給定的字節序列,因此咱們能夠用下面的grep指令來實現檢索:
在ROP中,以RET結尾的一系列指令稱爲gadget,若是咱們用如下順序覆蓋返回地址:libc的地址+ 0x22a12;「/bin/sh」的地址;libc的system()函數的地址,而後在執行下一個ret指令時,程序將彈出「/bin/sh」的地址到rdi,而後跳轉到系統函數,咱們就能夠達到咱們的目的。先輸入以下指令運行禁用了ASLR的victim程序:
再在另外一個終端中輸入以下指令:
咱們能夠看到,libc被加載到從0x7ffff7a1a000開始的內存中,所以gadget地址是0x7ffff7a1a000 + 0x22a12,「/bin/sh」的地址咱們以前已經找到了,是0x7fffffffed40,最後咱們經過下面的指令來找libc的system()函數的地址:
咱們能夠獲得libc的system()函數的地址是0x7ffff7a1a000 + 0x45730,最後將它們放到一塊兒,成功獲得了shell: