DEF CON CTF 2013 – diehard

DEFCON 2013 CTF - diehard - task description

Die Challenge “diehard” befasst sich mit dem gleichnamigen Film “Stirb Langsam” Dabei soll John McClane eine Bombe entschäfen, indem er Wasser innerhalb zwei verschieden großer Gefäße umfüllt.

Nach dem Verbinden zum Challenge-Server sehen wir zuerst jedoch nur folgende Nachricht:

rup0rt@lambda:~$ nc diehard.shallweplayaga.me 4001
Welcome John McClain...
You are standing at the start of your adventure. You will be tested
on your ability to solve a set of riddles in the minimum amount of
time possible.

As you glance around the room, you realize the problem you seek is
to the north. To the west is a mysterious field.
Exits: n w
>

Offensichtlich müssen wir uns zunächst durch ein Labyrinth im Text-Adventure-Stil arbeiten, bevor wir die “Rätsel” lösen können. Da uns der Weg “im Norden” jedoch bereits vorgegeben ist, navigieren wir promt in diese Richtung.

> n

You go n
You are standing in a long dark hallway. Rays of light flicker off the smooth surfaces around you.
Exits: n s
> n

[...]

You go n
You see a large room with sleek black walls on every side.  The ceiling overhead is smooth and devoid of features. In the center of the room is a small scale that links to a closed door.
A small fountain is gurgling water in the corner. The scale has a small inscription on it.
A red jug is sitting in the room.
A blue jug is sitting in the room.
A fountain is sitting in the room.
A scale is sitting in the room.


Nach mehreren Schritten, die jeweils nur eine Richtung ermöglichen, erreichen wir einen Raum, der uns alle Gegenstände für John McClanes Aufgabe bereitstellt. Mit dem Kommando “look” können wir dabei die Gegenstände genauer betrachten.

> look red jug
You look at a red jug in the room.
This shiny red jug appears to be a container made for holding water.
A red jug holds 0 of 3 gallons.

> look blue jug
You look at a blue jug in the room.
This shiny blue jug appears to be a container made for holding water.
A blue jug holds 0 of 5 gallons.

> look inscription
You look at an inscription.
It reads:
To get to the next stage put 4 gallons on the scale.

Damit haben wir alle Informationen, die zum Lösen dieser Aufgabe erforderlich sind. Es handelt sich um die selbe Aufgabe, die auch John McClane zu lösen hatte. Wir sollen mit Hilfe von zwei Krügen, die jeweils fünf bzw. drei Gallonen fassen, ein Gewicht von vier Gallonen erzeugen.

Mathematisch betrachtet muss die Zahl Vier durch Addition/Subtraktion der Zahlen Fünf und Drei entstehen. Im vorliegenden Fall sieht die Rechnung so aus:

4 = 5 – 1

4 = 5 – ( 3 – 2 )

4 = 5 – ( 3 – ( 5 – 3 ) )

Wir müssen daher den großen Krug (5) in den kleinen (3) füllen um zwei Gallonen im großen Krug zu behalten. Anschließend füllen wir die zwei Gallonen in den kleinen Krug um – nach erneutem Befüllen und Umschütten – noch eine Gallone im großen Krug zu erhalten. Erneutes Befüllen und Umschütten fürt zu vier Gallonen im großen Gefäß.

Dies muss nun noch dem Spiel durch Senden der korrekten Kommandos vermittelt werden:

> get blue jug
You get a blue jug.

> get red jug
You get a red jug.

> fill blue jug
You fill a blue jug with 5 gallons.
A blue jug now has 5 gallons.

> pour blue jug into red jug
You pour 3 gallons into a red jug from a blue jug.
A red jug is now full.

> empty red jug
You empty a red jug.

> pour blue jug into red jug
You pour 2 gallons into a red jug from a blue jug.
A blue jug is now empty.

> fill blue jug
You fill a blue jug with 5 gallons.
A blue jug now has 5 gallons.

> pour blue jug into red jug
You pour 1 gallons into a red jug from a blue jug.
A red jug is now full.

> put blue jug onto scale
You put a blue jug onto a scale.
The scale balances perfectly and a door opens to the next room!

Es funktioniert und die Tür zum nächsten Raum öffnet sich! Wie zu erwarten war, muss diese Aufgabe jedoch keinesfalls nur ein Mal gelöst werden. Auch die Größe der Gefäße variert und erfordert ein Python-Skript:

#!/usr/bin/env python
import socket
import time
import re

s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("diehard.shallweplayaga.me",4001))

toroom = "n\nn\nn\nn\nn\nn\ne\nn\nw\nn\n"
getinfo = "l red jug\nl blue jug\nlook inscription\n"
takeall = "get blue jug\nget red jug\n"

# fetch data
data = s.recv(2048)
print data

s.sendall(toroom)

count = 0
goal = 0

while 1:

  print "COUNT: ", count

  data = s.recv(4096)
  print data

  if "You have failed!" in data:
    exit(0)

  if goal == 0 and ("A scale is sitting in the room." in data or "Streams of dust" in data):
    goal = 1
    s.sendall(getinfo)
    continue

  for line in data.split("\n"):
    if "A key is sitting in the room." in line:
      s.sendall("look key\nget key\nl key\n")
      print "REQUEST SEND"
      data = s.recv(4096)
      print data
      break
    if "A red jug holds" in line:
      m = re.search('(?<= of )\w+', line)
      red = int(m.group(0))

    if "A blue jug holds" in line:
      m = re.search('(?<= of )\w+', line)
      blue = int(m.group(0))

    if "To get to the next stage put" in line:
      m = re.search('(?<= put )\w+', line)
      goal = int(m.group(0))

      print "red=", red, "blue=", blue, "goal=", goal

      lred = 0
      lblue = 0

      solution = takeall

      if red > blue:
        while lred != goal:
          if lred == 0:
            solution += "fill red jug\n"
            lred = red
          solution += "pour red jug into blue jug\n"
          if lred > (blue-lblue):
            lred -= blue-lblue
            lblue = blue
            solution += "empty blue jug\n"
            lblue = 0
          else:
            lblue = lred
            lred = 0
        solution += "put red jug onto scale\n"
        solution += "drop blue jug\n"
      else:
        while lblue != goal:
          if lblue == 0:
            solution += "fill blue jug\n"
            lblue = blue
          solution += "pour blue jug into red jug\n"
          if lblue > (red-lred):
            lblue -= red-lred
            lred = red
            solution += "empty red jug\n"
            lred = 0
          else:
            lred = lblue
            lblue = 0
        solution += "put blue jug onto scale\n"
        solution += "drop red jug\n"

      solution += "n\n"
      print "Sending " + str(len(solution)) + " bytes..."
      s.sendall(solution)
      count += 1
      goal = 0

s.close()

So wie eben ausführlich dargestellt, arbeitet auch das Skript. Es füllt die Gefäße so lange nach der Methode ineinander um, bis das Gewicht für die Waage entstanden ist. Dann wird das Gefäß auf die Waage gestellt, die Tür geöffnet und nach Norden gegangen.

(Am Ende muss der gefundene Schlüssel noch genommen (get) und betrachtet (look) werden. Diese Funktionalität ist mit dem Vorgriff auf das Wissen bereits implementiert.)

Die Ausführung des Skriptes wird zusätzlich dadurch erschwert, dass mit fortschreitender Zeit die Decke der Höhle einzustürzen beginnt. Das heißt, es kommt auch auf das Tempo an ;-). Nach einigen Anläufen war das Skript jedoch schnell genug und liefert diese Ausgabe.

You find yourself in a solid granite chamber filled with
hexadecimal writings on the walls.  In the middle of the
room sits a small key.
A key is sitting in the room.

> get key
You get a key.

> look key
You look at a key that you are holding.
An inscription on the key reads:
The key is: yippie kay yay motherfucker 3nc83n89fg

Die Lösung lautet somit “yippie kay yay motherfucker 3nc83n89fg“.

3 thoughts on “DEF CON CTF 2013 – diehard

  1. Hi.

    I’m not familiar with Python, I tried the same with PHP and every time I failed around ~ 16th round with timeout. The thing is socket by default has blocking mode, I couldn’t get all data till input easy. Nevertheless, non blocking mode didn’t give me any advantage as well.

    So I wonder how the fuck your script is faster? :p s.recv(4096) doesn’t wait till buffer (4KB) is full? Or it gets data as soon as possible? In this case you could fail, because data probably contain only part of string like “A scale is si”.

    But gj anyway!

    • Dude, I’ve tested your script right now, the same result:
      >The roof collapses in on you. You have failed!

      It highly depends on the Internet speed, it sucks tbh.

    • Hi,

      you are absolutely right! I also had to run the script multiple times because it was “too slow”. It heavy depends on your connection and the server load. So this challenge is not very fair… :-/

      s.recv(4096) fetches data from the socket up to a maximum of 4096. If there is less data it just fetches the amount waiting and stops blocking. That might be not the best solution (cf. select()) but I was lucky and could solve the challenge this way :-).

Leave a Reply

Your email address will not be published. Required fields are marked *