BioTerra CTF 2016: Schinken


The service encrypts a string with a password, into a pdf file (or the tex). You can notice that the letters of Lerem-ipsum have different fonts.

Actually, this fonts contain bits about the xor-value of the plaintext and the password. You can find this by trying pairs like: password is AAAA and text is AAAA, or password is AAA and text is ABCD. Especially, the only letters ABCDEFGHIJKLMNOPQRSTUVWXYZ{}_ are allowed, and the integers to xor is the indices in the letters (not ascii-code).

To decrypt it, pdf2ps helped me.

#!/usr/bin/env python2
from pwn import * #
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='command')
subparser = subparsers.add_parser('query')
subparser.add_argument('--name',     required=True)
subparser.add_argument('--address',  required=True)
subparser.add_argument('--text',     required=True)
subparser.add_argument('--password', required=True)
subparser.add_argument('--do',       required=True, choices=[ 'letter', 'source' ])
subparser = subparsers.add_parser('crack')
subparser.add_argument('--key', default='Geheim')
parser.add_argument('--host', default='')
parser.add_argument('--port', default=6969, type=int)
args = parser.parse_args()
context.log_level = 'debug'

def query(name, address, text, password, do):
    import base64
    p = remote(, args.port)
    p.recvuntil('Enter your name:')
    p.recvuntil('Enter your address:')
    p.recvuntil('Enter your text to encrypt:')
    p.recvuntil('Enter a password:')
    p.recvuntil('-----BEGIN MESSAGE----')
    s = p.recvuntil('-----END MESSAGE-----')
    s = base64.b64decode(s)
    return s

def crack(pdfstr):
    import re
    import subprocess
    p = subprocess.Popen(['pdf2ps', '-', '-'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    psstr, _ = p.communicate(pdfstr)

    msg = ''
    bit = []
    state = None
    for line in psstr.split('\n'):
        if'/R6 \S+ Tf', line):
            state = 0
        if'/R12 \S+ Tf', line):
            state = 1
        for s in re.findall(r'\(([^)]+)\)', line):
            for c in s.strip('()'):
                msg += c
                bit += [state]
    i = msg.index('Lorem')
    j = msg.index('Mitfreundlichen')
    msg = msg[i : j]
    bit = bit[i : j]'msg: %s', repr(msg))'bit: %s', repr(bit))'len: %d', len(bit))
    assert len(bit) % 5 == 0

    return bit

if args.command == 'query':
    s = query(, args.address, args.text, args.password,

    if == 'letter':
        import tempfile
        import subprocess
        import time
        with tempfile.NamedTemporaryFile(suffix='.pdf') as fh:

    elif == 'source':
        import itertools
        import re
        for line in s.split('\n'):
            if len(re.findall(r'\\fontfamily', line)) > 3:
                for i in itertools.count():
                    m ='^\\fontfamily{(\w\w\w)}\\selectfont (\S) ?', line)
                    if not m:
          '%d %s: %d', i,, { 'ppl': 0, 'phv': 1 }[])
                    if i % 5 == 4:
                    line = line[m.end(): ]

elif args.command == 'crack':
    with open(args.file) as fh:
        s = crack(
    flag = ''
    for i in range(len(s) // 5):
        a = int(''.join(map(str, s[i*5 : i*5+5])), 2)
        b = alphabet.index(args.key[i % len(args.key)].upper())
        c = alphabet[a ^ b]
        flag += c