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

Background

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:

  • Web (10 challenges)
  • Reverse Engineering (3 challenges)
  • Misc (3 challenges)
  • Forensics (1 challenge)
  • Crypto (2 challenges)
    1. T.A.R.D.I.S.
    2. Crypto2

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

T.A.R.D.I.S.

This is how the challenge looks like.

abe520df.png

The challenge presents a password verification time in microseconds whenever the attempt fails. Here’s how it looks like.

e8c1dd01.png

Looks what happen when the first digit is correct. The processing time increases.

c21354ac.png

I suspect the verification process looks at one digit at a time. With that in mind, I wrote a bash script to help automate the side-channel attack.

attack.sh
#!/bin/bash

HOST=challenges.bsidestlv.com
PORT=5050
NUM=10
HINT=$(perl -e "print '0' x $NUM")
HIT=""

function solve() {
  local csrf=$(curl -c cookie \
                    -s http://$HOST:$PORT \
               | grep csrf \
               | cut -d'"' -f8)

  local retn=$(curl -b cookie \
                    -s \
                    -d "password=$1" \
                    -d "csrf_token=$csrf" \
                    http://$HOST:$PORT \
               | grep 'class=message' \
               | grep -Eo '[0-9]+')

  rm cookie; echo $retn
}

for p in $(seq 0 $((NUM-1))); do
  FRONT=$(cut -c1-$((p)) <<<"$HINT" 2>/dev/null)
  BACK=$(cut -c$((p+2))-$NUM <<<"$HINT" 2>/dev/null)
  TIME=$(for n in $(seq 0 $((NUM-1))); do printf "%d:%d\n" $(solve ${FRONT}${n}${BACK}) $n; done)

  if [ $p -eq $((NUM-1)) ]; then
    HIT=${HIT}$(echo "$TIME" \
                | sort -t':' -k1n \
                | head -1 \
                | cut -d':' -f1)
  fi
    HIT=${HIT}$(echo "$TIME" \
                | sort -t':' -k1nr \
                | head -1 \
                | cut -d':' -f2)

  HINT=$(cut -c-$NUM <<<"${HIT}${HINT}")
done; echo $HINT

Let’s give it a shot.

e2fda688.png

This is what you see when you provide the correct password, 8105237467.

8fc6229e.png

The flag is BSidesTLV{7456urtyifkygvjhb}.

Crypto2

This is how the challenge looks like.

0f0ce266.png

Let’s take a look at what we are dealing with.

49baf6fc.png

Notice that it’s not a single quote but an apostrophe (or right single quote)?

7b63efeb.png

In any case, is represented by three bytes: \xe2\x80\x99. I’m not sure if this observation is going to be useful now, we’ll see. Suffice to say, the content of Anorak’s Invitation.txt is not human-readable.

a1cfd101.png

Anorak’s Invitation is a video game message from James Halliday, the creator of OASIS in the book/movie “Ready Player One”. Although I’m familiar with the movie, having watched it not too long ago, I’m not too familiar with the book.

According to the challenge’s hint, the creator is not venturing beyond basic ciphers; we can assume the use of basic cryptosystem such as substitution, Caesar’s cipher, etc, which leaves the punctuation marks untouched.

Let’s perform some basic analysis on the bytes using the Unicode representation of English punctuation marks such as apostrophe \x32\x80\x99, period and a single space thereafter, \x2e20, left double quote \xe2\x80\x9c and right double quote \xe2\x80\x9d.

apostrophe

# xxd -p encrypted.txt | tr -d '\n' | grep -Eo 'e28099' | wc -l
76

period and a single space

# xxd -p encrypted.txt | tr -d '\n' | grep -Eo '2e20' | wc -l
101

left double quote

# xxd -p encrypted.txt | tr -d '\n' | grep -Eo 'e2809c' | wc -l
32

right double quote

# xxd -p encrypted.txt | tr -d '\n' | grep -Eo 'e2809d' | wc -l
31

Sure smells like English text to me. From here, it’s not hard to deduce that the creator used the original text of “Ready Player One” as the plaintext, given the hint Anorak’s Invitation.txt. If only I can find the original text in the book!

The biggest challenge now becomes finding the correct text from the book to launch a known-plaintext attack (KPA) against the cryptosystem. Despite my sincerest effort, I can only find the first two chapters of the book. It’s a copyrighted book after all. :wink:

Assuming the creator bought the book, the original text must be from a legitimate source. Long story short, I’ve painstakingly put together the plaintext, also known as crib, from the Prologue of the book.

4d524b61.png

The encrypted flag is at file offset 0x44b3 with a length of 22 characters.

Armed with the plaintext, we can use the following Python code, along with it’s builtin dictionary structure to build a codebook:

from itertools import izip

s = ''
dic = {}
n = 0x44af    # this is where the ciphertext or plaintext ends

encrypt = open('encrypted.txt', 'r').read()
decrypt = open('plaintext.txt', 'r').read()

for (c,d) in izip(encrypt[:n], decrypt[:n]):
  dic[c] = d

for x in encrypt[-22:]:
  s += dic[x]

print s

5c1f67cf.png

I’m definitely on to something! Let’s assume that \xbb and \xd4 represents { and } respectively.

d49a0e88.png

Looks like I’m on the right path. Let’s put an underscore _ to represent \x9b (it’s the only occurrence by the way).

fdc4eca7.png

By way of inductive reasoning, I think we can infer that the character represented by the underscore is also a digit. From here, it’s trivial to use trial-and-error to get to the last digit. We supply the flag, stepping up or down, one at a time, depending on which digit you start with first, to CTFd, and see which one is the correct one.

The flag is BSidesTLV{49489416671}.