ROP攻擊技術

原文地址:


http://blog.csdn.net/linyt/article/details/48738757



背景

前面介紹了ret2libc和ret2plt,這兩個攻擊技術的相通點是函數參數是通過壓棧來傳遞,這也是i386架構的調用約定。然而隨着64位服務器的普及,以及後來越來越廣泛,以致幾乎所有服務器都升級到64位的硬件上。

x86_64天生具有免疫力

根據X86_64 ABI的調用約定函數間傳遞參數不再以壓棧的方式,而是以寄存器方式傳遞參數,前面6個參數依次以rdi, rsi, rdx, rcx, r8和r9寄存來傳遞。

Linux系統,64位架構只使用48位的虛擬地址空間,也即每個地址高16位全部爲0,因此在64位系統上,地址已天然零化。

前面介紹的攻擊方法似乎一夜之間沒有用武之地,很快安全人員在ret2plt攻擊方法基礎上,做了一個升級片本的攻擊方法,稱爲ROP(Return-oriented programming)方法。

何謂ROP

顧名思義ROP,就是面向返回語句的編程方法,它借用libc代碼段裏面的多個retq前的一段指令拼湊成一段有效的邏輯,從而達到攻擊的目標

爲什麼是retq,因爲retq指令返到哪裏執行,由棧上的內容決定,而這是攻擊者很容易控制的地址。

那參數如何控制,就是利用retq執行前的pop reg指令,將棧上的內容彈到指令的寄存器上,來達到預期

一段retq指令未必能完全到想攻擊目標的前提條件,那可在棧上控制retq指令跳到另一段retq指令表,如果它還達不到目標,再跳到另一段retq,直到攻擊目標實現。

在ret2plt攻擊方法,我們使用PPR(pop, pop, ret)指令序列,實現順序執行多個strcpy函數調用,其實這就是一種最簡單的ROP用法。ROP更是ret2plt的升級版

ROP方法技巧性很強,那它能完全勝任所有攻擊嗎?返回語句前的指令是否會因爲功能單一,而無法實施預期的攻擊目標呢?業界大牛已經過充分研究並證明ROP方法是圖靈完備的,換句話說, ROP可以借用libc的指令實現任何邏輯功能。

攻擊實例

在這裏省去對準EIP以及漏洞代碼分析,直奔主題,如何構造ROP指令順列來實現攻擊邏輯。

簡單起來,攻擊目標爲實現system(“echo success”) 這個函數調用。

首先,調用system的參數爲”echo success”字符串的地址,而字符串是棧注入的內容,那它的地址應該是rsp + offset。而函數調用時,第一個參數是放到rdi寄存裏面。 所以需要從libc裏面,在retq或者call *reg指令前找到rdi = rsp + offset邏輯等價的指令序順,發現有如下的兩條指令:

0x7ffff7a610a3      lea 0x120(%rsp),%rdi
0x7ffff7a610ab      call    %rax
  • 1
  • 2
  • 1
  • 2

這樣,可以將”echo success”字符串安排在rsp + 0x120的位置。但往下一條指令,要指令call %rax,因此需要在指令這個指令段前控制rax的值必須爲system函數的地址。

然後,想將system地址放到rax相當容易,只需要在retq指令令,找到pop rax指令即可,從libc裏面查找,發現如下:

0x7ffff7a3b076:     pop %rax
0x7ffff7a3b077:     pop %rbx
0x7ffff7a3b078:     pop %rbp
0x7ffff7a3b0f9:     retq
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

於是,構建如下的執行順序:

pop %rax <— 這裏彈出system函數地址 
pop %rbx 
pop %rbp 
retq <— 這裏從棧中跳到下段指令

lea 0x120(esp), %rdi <– 需要安排好”echo success”位置,使得此時的rsp + 0x120剛好是字符串地址 
call *%rax <– 調用system,參數剛好。

通過gdb查看system函數的地址:

(gdb) p system
$4 = {<text variable, no debug info>} 0x7ffff7a61310 <system>
  • 1
  • 2
  • 1
  • 2

於是棧注入內容就很容易: 
0x7fffff7a3b076 + address of system + dummy1 + dummy2 + 0x7ffff7a610a3 +dummy(0x120) + “echo success”

攻擊圖示

上面的攻擊實例中,指令的執行過程和棧注入內存佈局如下圖所示。

ROP實例棧結構和指令執行過程

ROP可以爲所欲爲

上面提到已有研究員稱ROP攻擊借用的多個代碼片段串起來的程序邏加是圖靈完備的,也即這個程序包含順序執行語句(這個當然是廢話),還有分支語句,甚至有循環語句。

稍有反編譯或者逆向工程經驗,或者對C語言生成的彙編結構熟悉都知道,retq指令是函數的返回指令,在此之前的指令是彈棧指令(如pop rax, pop rbx等),怎麼可以出現分支指僅(bne等),甚至循環指令呢?

是的,這個是事實,但不是事實的全部。如果將glibc進行逆向工程,會發現retq指令前向全是清一色的pop指令,但是事實上攻擊者總是不按常規出牌。

X86指令集是CISR指令集,密集度很高,一條指令中的一部分,也可能是一條新指令。

攻擊者就是利用這一點,不按常規出牌。retq指令就只有一個字節,是C3。通過編寫工具,對glibc進行掃描,把C3的指令內容找到,然後向前解碼各種可能的指令,形成一個指令表。這些指令表會是非常豐富,有運算指令,轉跳指令,以及訪存指令。利用它們可以形成圖靈完備的計算邏輯。


關於ROP 的攻擊的具體實現:


具體實施ROP 攻擊的時候,由於找到所有合適的 gadgets 非常困難,花費的時間也很多。所有某些情況下,會 找到gadgets 去remove 掉 DEP , 使堆棧有執行權限,進而方便實施 buffer overflow 攻擊。 這個方法會簡化攻擊難度。






發佈了102 篇原創文章 · 獲贊 28 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章