IceCTF 2015: PyShell
Category: exploitation - Points: 100
Description: Daniel is running this server which allows you to evaluate basic python expressions. It’s clear that he’s tried to secure it though. Can you see if you can get it to print the flag? You can access it by running
nc vuln2015.icec.tf 8000
.Hint: Even if some keywords are banned, is that gonna stop you?
The task comes with the file shell.py
which is the source code of the webservice available at the specified location. The source is the following:
So we are provided with a shell and the user input is checked not to contain one of the banned words before being passed to the exec
function which dynamically execute the code.
In order to increase the shell security, every element but print
and raw_input
is removed from the __builtins__
dictionary which contains the common functions.
Background
This reminds us the CSAW2014 problem pybabbies and its solutions can be adapted to this task. There are a few write-ups for that task, but we can groups them in two categories:
- obtain a reference to the
file
function and guess the filename for the flag; - obtain a reference to the
os
module in order to use theos.system
function and be able to issue commands to the underlying system.
The second way is more interesting, but even if the methods used to gain the reference to the os
module are really interesting, they are quite complicated such as this and this. Most of them are based on linecache
, contained in warnings.catch_warnings
, which contains a reference to os
united with some techniques explained in this amazing Ned Batchelder’s post.
Another solution
We found a more straightforward solution for this task. Since in this case the sys
module is already loaded, we can use sys.modules
to get the references of all the loaded modules (including os
).
The problem here is that sys
is a banned word. We want to clear the banned
variable, without being able to use its name. It would be great if there was a dictionary containing all the globals so that we can compose the name of the variable with string operations like 'ban' + 'ned'
and evade the blacklist.
The dictionary that contains the global variables can be accessed with the function globals()
, but unfortunately we don’t have access to that variable since the built-ins were cleared.
We can access the same dictionary defining a function
object (for example using a lambda
) and accessing its __globals__
variable. This way we will be able to set the banned
variable to an empty list, disabling the blacklist:
Now we don’t have a blacklist anymore, so we can easily get a reference to os
:
Now we can list the files in the directory and print the flag:
So the flag is not_your_average_python
.