In what has become something of an annual tradition, PPP once again ran Plaid CTF over CMU's Carnival break. This year's theme was Hackers and time travel, with problems to match. In this post, I'm going to talk about a problem I wrote whose content relates to things I've posted about here before.


Yes, I'm going to talk about Freya. At 250 points in the "Misc" category, it doesn't seem too imposing. (Assigning point values to problems is a discussion for another time, but only five teams solved this one despite it being open for about half of the CTF.)


The problem text (thanks, Tyler!) reads:

We've traveled back far, but this protocol looks familiar... Our reconnaissance team did a great job, they got us a data capture from the currently running systems and a private key from the server (shell.woo.pctf which resolves to Take a look at the traffic our reconnaissance team picked up, and see if you can get access to The Plague's server, at

The server mentioned in the problem description is no longer running, but distribution of the IP-DNS relationship was necessary because not only did the problem include a pcap file, but also because I decided to use the non-existent TLD "pctf". Mostly this was done out of laziness.

Anyway, the problem also included two .pem files (a cert file and a private key file) and a file called "password". "password" just contains the string "shellpls", and upon closer inspection, the cert file has been provided just for convenience; it was generated from the private key file.

Intended Solution

The first thing I did was to add an entry for shell.woo.pctf to my /etc/hosts file, but strictly speaking this isn't necessary. It does simplify everything else a bit, though.

Well, there's a pcap file, so it seems reasonable to start by opening it up in your traffic inspector of choice (I used wireshark). So we do that and it looks like this:

Encrypted Traffic

I've highlighted a packet from the TCP session on port 443. Scroll through, and there's also a TCP session on 22. The first looks suspiciously like HTTPS, and the second looks like SSH, which wireshark agrees with. So we tell wireshark to decrypt traffic using the private key.

If you've never done this before, the procedure looks something like this:

Open wireshark. From the "Edit" menu, choose "Preferences...", then expand "Protocols". Scroll down and select SSL. Set an "SSL debug file:" and be sure that both "Reassemble SSL records spanning multiple TCP segments" and "Reassemble SSL Application Data spanning multiple SSL records:" are checked. Then next to "RSA keys list:" click the "Edit..." button. Click new, then enter the IP address of the server. For "Port:", enter 443, and for "Protocol:", enter http (not https). Select the "Key File", and leave the "Password:" field blank. Then click "OK", "Apply", "OK, "Apply", and "OK".

(The source of that procedure is my earlier post, which contains spoilers.)

Anyway, you do that and you get this:

Decrypted Traffic

Roughly the same packet is highlighted for inspection. There are a few important things to notice here, including "application/kerberos", and the POST over HTTP{,S} nature of the traffic. The very observant will notice the "/kkdcp" destination of the POST, but that's not strictly necessary. Regardless, one should find Microsoft's technical document on MS-KKDCP which outlines the protocol.

To get the key, you need to understand the protocol. I've already written code that does this; integration is planned into MIT Kerberos, an effort for which I am very grateful to my co-workers nalind, npmccallum, and (my mentor) adamiyo.

Even speaking the protocol, though, you're still not done. See, the Kerberos stuff we wrote does validation of certificates, and the Freya server uses a garbage self-signed thing. The easy solution (please do not do this!) is to add the certificate to the system security store. A better solution (which I used) was to disable the verification in the build of Kerberos (and then not use the build for anything else).

Once you get a TGT against the server, you also need to get a service ticket (for "host/shell.woo.pctf"). Then you can ssh in, and the server prints the key. By the way, the stanzas that need to be added to /etc/krb5.conf look like:

  WOO.PCTF = {
    kdc = https://shell.woo.pctf/kkdcp
    default_domain = woo.pctf


  .woo.pctf = WOO.PCTF
  woo.pctf = WOO.PCTF

The result of this process was that I had an easy check for whether Freya was up during the competition (thankfully, it always was). It looked like this:

PATH=/opt/bin:$PATH time ( \
  echo "shellpls" | kinit -S host/shell.woo.pctf ppp@WOO.PCTF;
  ssh ppp@shell.woo.pctf

Other Solutions

Since we require writeups in order to collect on prizes, I have writeups from Dragon Sector and 0xffa for how they did the problem.

0xffa (congratulations for winning first place) found the protocol, and additionally used the same git tree. One oddity is that rather than explicitly requesting a service ticket from the server, they pass "-K" to ssh (to become ssh -K ppp@shell.woo.pctf); in other words, ssh will perform the service ticket acquisition rather than kinit. The patch they use is also slightly different than the one I used, but the difference is minor.

Dragon Sector (congratulations for winning second place) did not find the protocol, and this is really interesting to me. Their solution involves a proxy server that performs translation on standard traffic, but notably it's not this thing I made for testing. Instead, they used their proxy does this:

We started proxying custom kinit requests for the key (rewriting kerberos protocol to HTTPS and back) in order to obtain a new kerberos key, but something wasn't right. It seems that the HTTPS requests and replies were encapsulated in some kind of container, and carried additional bytes at the beginning of each packet (9 and 12 bytes in those HTTP requests respectively). So, we simply added those bytes with our proxy (a simple net/file proxy written in C), taken from the original TCP/HTTPS session visible in wireshark. We also removed those unnecessary bytes from the KDC responses so kinit could interpret them.

More than anything, I think I'm impressed that this works. The bits at the end they remove are going to be the realm string, which indeed is not needed. The bits at the beginning are trickier; they include ASN.1 encoding information. Anyway, using a local proxy means that that the krb5.conf they use is different than the one I used. One final oddity is that they did not make an /etc/hosts entry; instead, they expand the ssh line, which is then

ssh ppp@ \
  -o GSSAPIKeyExchange=yes -o GSSAPIServerIdentity=shell.woo.pctf

Server Configuration

The shell server was running a variant of kdcproxy that had been tweaked to not be WSGI. sshd_config was also modified to disable non-GSSAPI, non-pubkey logins.

A second server (that was not world-facing!) was configured using FreeIPA, and I used its Kerberos setup as the backend for the shell server.

Both machines ran Fedora due to FreeIPA constraints, which complicated our infrastructure (almost everything else was Debian).

Some Thoughts

One of the things I feel is consistently missing from the CTFs we play is interesting networks problems. I used this problem because it was based on material I knew extremely well, so I could confidently answer questions and gauge where people were getting stuck. That said, I did not think this was a particularly difficult problem. To be fair, I had spent the summer prodding at the protocol, but this means that my code was already out there.

I suspect that most people got stuck at the part where traffic needs to be decrypted. Wireshark does not make this feature obvious, and I wish it did because it's extremely useful for testing and reversing. I think that many people also approached the problem by extracting the IP of shell from the pcap, and then were confused when shell (as per spec!) dropped malformed requests on the floor. This was the source of most of the "is this up?" questions I received.

I will be making more of these. I can't guarantee that the point value estimation will be improved to any degree, but networks and protocol security are not going away. I feel these important topics in security, so: expect them.