33C3 CTF: Fun times

pdfmaker (75 points)

The first challenge I tried was pdfmaker. Surprisingly I spent way too much time on this simple starter challenge. I initially planned to use this challenge as a warm up but ended spending about 10 hours on it, mostly due to me overlooking simpler solutions that are obvious in hindsight. On the route to this challenge I learned a lot about TeX and LaTeX that I honestly would not have needed to know and even started asking questions on Stack Overflow.

The pdfmaker service allows you to upload tex files, sty files, and a bunch of other types of files and to compile tex files. The service also allows you to view generated log files. Interestingly, when uploading files the text is filtered and lines containing .., /, or \x are removed. The removal of the first two types of lines is obvious as the flag is available in ../../flag but the last one took me longer to figure out. So the challenge is to write a LaTeX program that opens the flag and prints it to the log file. What makes the challenge harder is that the LaTeX installation does not have any auxiliary packages available, so the LaTeX document has to be self sufficient.

Overcoming the .. restriction is easy as two . can easily be concatenated. Overcoming the / restriction was much much much harder. I soon figured that the log file contains 2015/Debian in the first line which contains a / character that we could use. The next question is how to access a substring or character inside a string through LaTeX which is surprisingly hard and took me way too long to figure out. I assumed that we cannot use libraries but what I forgot was that we can upload libraries. (This sounds simple but was an amazing aha moment for me after spending such a long time on this challenge.)

First, we upload a simple tex file first.tex and compile it to generate a log file create tex first:


Then we compile it compile first and upload a second tex file create tex second:

\immediate\read\file to\fileline
    \read\file to\fileline

This LaTeX file opens the first.log log file and uses the ystring library to extract the 62nd character, concatenates the individual components to open the flag and print it to the log file. Now, to use the ystring library we need to upload it. We start by copying xstring.sty and xstring.tex to local files and replace all occurrences of xs with ys to overcome the last filter restriction and then upload these two files as well. After compiling compile second we can access the log file and get the flag. The log file can then be displayed through show log second and we get the flag: 33C3_pdflatex_1s_t0t4lly_s3cur3!

Note that I've also went through the trouble to get a stack exchange account and asked my LaTeX question there.

exfil (100 points)

Exfil was a fun forensics challenge that asked us to extract data from a pcap. The pcap file contains a remote shell session of an attacker that is obfuscated through an UDP/DNS channel that includes a lot of redundancy. Thankfully we received the server along with the challenge which made decoding much easier. I wrote a Python-based decode that dumps both the server side stream and the client side stream to files. The client side stream allowed me to extract a private key and the server side stream allowed me to dump server.docx.gpg which could then be decoded with the extracted private key:

#!/usr/bin/env python
from dnslib import *
import dpkt
import base64

domain = 'eat-sleep-pwn-repeat.de'

def decode_b32(s):
    s = s.upper()
    for i in range(10):
            return base64.b32decode(s)
            s += b'='
    raise ValueError('Invalid base32')

def parse_name(label):
    return decode_b32(b''.join(label.label[:-domain.count('.')-1]))

def parse_nameH(label):
    label = label[:-len(domain)-2]
    label = label.translate(None, '.')
    return decode_b32(label)

sseq = 0
sack = 0
cdata = b''
sdata = b''

f = open('dump.pcap')
pcap = dpkt.pcap.Reader(f)
for ts, buf in pcap:
    #print(ts, len(buf))
    eth = dpkt.ethernet.Ethernet(buf)
    ip = eth.data
    udp = ip.data
    #dns = dpkt.dns.DNS(udp.data)
    #for qname in dns.qd:
    #    print binascii.hexlify(parse_name(qname.name))
    query = DNSRecord.parse(udp.data)
    packet = parse_name(query.q.qname)
    conn_id, seq, ack = struct.unpack('<HHH', packet[:6])
    data = packet[6:]
    #print data
    if seq == sack:
        #print('s %d %d %d\n' % (conn_id, seq, ack))
        sdata += data
        #print data
        sack += len(data)
    for x in query.rr:
        pack2 = parse_nameH(str(x.rdata))
        conn_id2, seq2, ack2 = struct.unpack('<HHH', pack2[:6])
        data2 = pack2[6:]
        if seq2 > sseq:
            cdata += data2
            print data2
            sseq = seq2
        if seq2 == sseq and len(cdata) < sseq:
            cdata += data2
        print('x %d %d %d %d %d\n' % (conn_id2, seq2, ack2, sack, sseq))

        #if ack2 > sseq:
        #    forget = ack2 - sseq
        #    #cdata += data2
        #    sseq += forget
        #    #print('c %d %d %d\n' % (conn_id2, seq2, ack2))
    #x = decode_b32(udp.data[13:udp.data.index('eat')-1])

print "sdata"
print sdata
print "cdata"
print cdata

secret = sdata[sdata.index('START_OF_FILE')+13:sdata.index('END_OF_FILE')]
with open('secret.docx.gpg', 'w') as f:

Running the decryption gpg --decrypt secret.docx.gpg > secret.docx then yields the flag 3C3_g00d_d1s3ct1on_sk1llz_h0mie.

0x90 (150 points)

The 0x90 challenge was a fun blast from the past where we connected to a Slackware 1.01 instance from the early 90ies that had a vulnerable version of lpr with a known privilege escalation. So we could simply google for it and just reuse the exploit.

Uploading the exploit is fun due to the weird remote connection not pasting escape characters correctly. But I somehow managed to open vi, go into edit mode, paste the C file, compile it through gcc exp.c and run it. The flag can then be accessed through cat /flag.txt and yields 33C3_Th3_0x90s_w3r3_pre3tty_4w3s0m3.


Overall, this was a fun CTF but I spent way too much time on the pdfmaker. I've also looked at the someeta1 challenge which looked like fun but did not have enough time to decode all the templated until the CTF was over.