This post documents my attempt to complete BSidesTLV: 2018 CTF (Misc). If you are uncomfortable with spoilers, please stop reading now.

On this post


The 2018 BSidesTLV CTF competition brought together over 310 teams burning the midnight oil to crack our challenges in a bout that lasted for two weeks. You can now enjoy the same pain and suffering, using this easy-to-use, condensed VM that now hosts all our challenges in an easy to digest format. The CTF has five categories:

What follows is my humble attempt of cracking the challenges in the Misc category.


This is how the challenge looks like.


After logging in, this is what I found.


If I had to guess, I would say that’s a Unix socket connected to a Docker. And since I’ve the SSH credentials, I can make use of SSH local port forwarding and connect to the Docker like so.


Look Ma, got access to Docker API without using Unix socket.


The stopped container with the image galf is highly suspicious. On second look, notice the reverse of galf is flag? This must be it.

My attempts to start the container resulted in an error.


I came across the command to export the entire container as a tarball after consulting the API.


After extracting the files from the tarball, the flag is at /home/flag_is_here/flag.txt.


The flag is BSidesTLV{i_am_r34dy_t0_esc4p3_th3_d0ck3r!}.


This is how the challenge looks like.


Let’s visit the challenge URL.


Appears to be a web shell. Turns out that it doesn’t accept alphanumerical characters and $&|'<>.

Look what happens when I supply a bad character?


Now look what happens when I supply the tilde ~ character?


In bash, the tilde ~ character represents the home directory of the current user. I suspect “the other side” is echoing out shell output, whatever “the other side” is.

In that case, I should be able to use shell wildcards. The wildcard ? and * represents single character and zero-or-more characters respectively.

Using these two wildcards, I was able to map out where the flag is.


The problem now is this—how the f**k do I display the flag with cat? With backticks `...` (or command substitution) and wildcards of course!

We know cat is at /bin/cat. We can use /???/??? to represent it. Of course, there are other directories and commands behind that pattern. But, when you surround it with backticks, the shell should execute the command and skip the rest of the non-executable. Let’s give it a shot.


The flag is:



This is how the challenge looks like.


The aim of this challenge is to escape the sandbox and run the following code.

import os; os.system("curl secret/flag.txt")

That’s all. No more no less.

How do we crack this challenge then? Remember Python’s axiom? Everything is an object. CPython provides special methods to get/set attributes from/in an object. Essentially, CPython allows shortcuts or syntactic sugar for a simple statement such as import os. Under the hood, it’s all special methods and/or special attributes at work.

For example, let’s say you want to assign the integer 1 to variable a. This is how you do it in Python 2.7.

>>> a = 1
>>> a

Or, you can do it this way.

>>> __builtins__.__setattr__("a", 1)
>>> a

Which is another way of saying, “I’m setting an attribute with a name of ‘a’ and value of 1 in the __builtins__ module.” __builtins__ is a module (an object by the way) that provides direct access to all the ‘built-in’ identifiers of Python. Use __builtins__.__dict__ to view all the attributes of this object.

Armed with this simple introduction, how do we run the above code? Again, the challenge has provided the much-needed hints.


If you look at the subclasses that inherit from object, you’ll find that warningmessage is one of them.


Dig deeper into the source code of the warnings module, you’ll see that the warnings module imports the linecache module.


Further down the code, you’ll find the warnings.WarningMessage class.


We can continue into the source code of the linecache module, and you’ll see that it imports the os module.


In summary, we can expand the code above like this.

obj = __builtins__.__class__.__mro__[1]
sub = obj.__subclasses__
war = sub()[58]
ini = war.__init__
glo = ini.func_globals
lin = glo["linecache"]
dic = lin.__dict__
ops = dic["os"]
run = ops.["system"]
run("curl secret/flag.txt")

Of course, certain words are still banned from use by the firewall. Recall how Python performs a = 1 under the hood? It’s actually setting an attribute in the __builtins__ object. With that in mind, we can make use of another trick to bypass the firewall—first break up the banned words and then combine them back through concatenation. Python uses the + operator for string concatenation. In fact, Python uses __add__ method-wrapper internally to do that.

For example, I can represent “curl” with "cu".__add__("rl").

Here’s the convoluted version. Warning: lots of typing ahead.

__builtins__.__setattr__("obj",__builtins__.__getattribute__("__class".__add__("__")).mro(  ).__getitem__(1))
__builtins__.__setattr__("war",sub( ).__getitem__(58))
run("cu".__add__("rl	sec").__add__("ret/fl").__add__("ag.txt"))

I’ve also replaced space (0x20) with tab (0x09) to bypass the firewall.


The flag is BSidesTLV{I_AM_The_Python_Master}.