Exodus Adventure CTF 2015

This is a walkthrough of the 2015 Exodus Adventure CTF.
The CTF takes the form of a text adventure game. Players are provided with an x86 Linux ELF binary of the game and a web page that runs the same binary inside a javascript x86 emulator called JS Linux.
Let’s get started.

,.        ___________                .___
\%`.      \_   _____/__  _______   __| _/_ __  ______
 `.%`.     |    __)_\  \/  /  _ \ / __ |  |  \/  ___/
   `.%`.   |        \>    <  <_> ) /_/ |  |  /\___ \
     `.%`./_______  /__/\_ \____/\____ |____//____  >
       `.%`.    __\/      \/          \/          \/
         `.%`.  \ \__       .___                    __                     ._.
           `.%`./ /_ \    __| _/__  __ ____   _____/  |_ __ _________  ____| |
             `./ //_\ \  / __ |\  \/ // __ \ /    \   __\  |  \_  __ \/ __ \ |
            __/ /:/;.  \/ /_/ | \   /\  ___/|   |  \  | |  |  /|  | \|  ___/\|
            \__/  `:/;./\____ |  \_/  \___  >___|  /__| |____/ |__|   \___  >_
                    `:/;.    \/           \/     \/                       \/\/
                      `:/;.,
                        `:/ ;
                          `'

You are in a dark cave. It smells like something hairy lives here.
You can see light to the north.

Exit is north.
> n
You can see shadows on the cave walls.

There is a bear here.
Exits are north and south.
> look bear
You see a cave bear, probably the last one alive.
> kill bear
With what? Your bare hands?
y/n? y
Congratulations! You killed the bear with your bare hands.
You find a flag on the corpse.
> inventory
You are carrying:
a flag
> look flag
You see a white silk flag with screen-printed lettering which reads:
71948222c27c8927a11d67f16167c47b.

This is the easy flag worth only 12 points. The description of the flag is stored in the adventure binary XORed with “xyzzy” (repeating) so that it doesn’t show up in plain text.
We continue north.

> n
You find yourself under a huge dome of rock with light coming in through an
opening too high up to reach.

There is a sign here.
Exits are north, south, east and west.
> look sign
To the east you see a sign which reads:
Klein's Maze
The only exit is the only entrance, but beware the path you take.
> e
You are in a little maze of twisting passages, all different.

Exits are north, south, east and west.
>

This flag is worth 150 points. You must carry the flag you got from the bear through the maze taking the path e-n-e-w-s-n-e-w-s-n-e-w-s-n-e-w-s-n-e-w-s-n-e-w-s-n-e-w-s-n-e-w-s. There are no walls in the maze; from each room you can go any direction. There are 5 rooms that form various loops inspired by the shape of a Klein bottle.


Each move after entering the maze is fed into a CRC32 checksum. Once the checksum is equal to 0xDBC67E22, this happens:

A wild Amatus appears!
Amatus looks pleased and you become aware of a tingling in your pocket.
Before you can react he vanishes in a puff of entropy.
> look flag
You see an orange flannel flag with machine-embroidered lettering which reads:
d2ae9f4db70cd6949fe824bb3793a651.

If you do not have the flag from the bear:

He looks at you circumspectly, shakes his head, and turns to dust.

The value of the flag is generated from the checksum. Since this value is known to be 0xDBC67E22 at this point, it’s easy to work out the value of the flag without “solving” the maze. Or, using a debugger, you could just break right before the comparison and set the value of the checksum to the expected value and continue execution to generate the flag.
Going west a few times exits the maze and we continue our adventure.

You find yourself under a huge dome of rock with light coming in through an
opening too high up to reach.

There is a sign here.
Exits are north, south, east and west.

> n
You are in a server room. There are four rows of cages packed with Dell, HP,
and Sun hardware.

There is a PE2650 here.
There is a VP here.
Exits are north and south.
> look PE2650
You notice a PowerEdge 2650 with a message scrolling on its LCD:
All Hail Eris.
> hack PE2650
You pull out your evil-maid USB stick but notice all the USB ports are covered
with epoxy.

This flag is worth 150 points. The “hack” function calculates the Collatz chain length for 42, verifies that it is indeed 8, then denies you access. The solution is to bypass this check and execute the “dead” code which generates the flag.
Next we continue deeper into the server room.

> n
At the back of the server room you see a cat-5 cable running down through the
opening of a displaced floor tile.

Exits are south and down.
> d
A staircase spirals down under the server room. You smell something sweet and
suspect it's a halon leak, perhaps you shouldn't spend a lot of time down here.

Exits are up and down.
> d
In the basement is a row of car batteries connected to what you can only assume
is a UPS. Next to the UPS is a rack with an Oracle server.

There is a terminal here.
Exit is up.
> look terminal
You see a rackmount terminal with a blinking cursor.
> type terminal
SQL> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
MySQL Error 1064: You have an error in your SQL syntax near 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa080581f00804b38a08053f2208052f8f'
SQL> SQL>
SQL> select * from memory where address = 0x080581f0;
0x9af58cab
SQL> select * from memory where address = 0x0804b38a;
0xa81a664b
SQL> select * from memory where address = 0x08053f22;
0xf40e3585
SQL> select * from memory where address = 0x08052f8f;
0x6e696f64

The flag is the concatenation of these 4 DWORDS from memory, and is worth 150 points. The mock SQL REPL is vulnerable to a missing null termination if you completely fill up the 80-byte buffer with input. The function which prints the error message echoes your input and reads past the end of the buffer where you see something that looks like four memory addresses. The values at these addresses are static except for the first one which is used in the maze code for storing the checksum. It is 0 when the program starts and is 0xFFFFFFF after leaving the maze. At the beginning of the SQL function however it is XORed with 0x650a7354 to give you 0x9af58cab. This is quite easy to solve with just a disassembler as the function directly references all these memory locations to print them into the space on the stack after the input buffer. To befuddle those using a disassembler, the last DWORD is in the middle of the string “You’re doing it wrong.”
Next we run back up the staircase and out of the cave to get some fresh air.

> u
A staircase spirals down under the server room. You smell something sweet and
suspect it's a halon leak, perhaps you shouldn't spend a lot of time down here.

Exits are up and down.
> u
At the back of the server room you see a cat-5 cable running down through the
opening of a displaced floor tile.

Exits are south and down.
> s
You are in a server room. There are four rows of cages packed with Dell, HP,
and Sun hardware.

There is a PE2650 here.
There is a VP here.
Exits are north and south.
> s
You find yourself under a huge dome of rock with light coming in through an
opening too high up to reach.

There is a sign here.
Exits are north, south, east and west.
> w
You are in a long narrow passageway, there is a strong breeze from the west.

Exits are east and west.
> w
You are standing outside. There is a cave to the east.

Exits are north, south and east.
> s
You are on a long twisting mountain path.

Exits are north and south.
> s
You are standing outside a yurt near the top of a mountain.
There is a path leading down to the north and up to the south.

Exits are north, south and east.
> e
You are inside a large yurt. You see filing cabinets, stacks of fan-fold
tractor-feed printouts piled on pizza boxes, a bug collection, and a jar
labeled "developer tears."

There is a wizard here.
Exit is west.
> look wizard
You see a gray old wizard, he doesn't have a long beard or a pointy hat,
but you can see the wisdom and authority in his eyes.
> talk wizard
The wizard looks deep into /dev/urandom and says: antilacrijudiintrazakordo.
> talk wizard dudatacodudanosemosaqueso
The wizard scratches many letters and numbers onto a piece of paper and hands
it to you. As soon as you read "5085d650d97d48bfc91c4fc93793a651" the paper
slips out of your hands as if it had passed right through them and dissolves
before reaching the floor.

This flag is worth 150 points but turned out to be the most difficult. The wizard gives you a random 30-bit number,

n

, encoded as 6 “magic words.” The wizard then expects a 30-bit number back from you,

x

, and computes

(x - n) % 65413

and checks that this value is equal to

26347

. He then generates the flag from

(x - n)

. Since there are 16,415 30-bit numbers which satisfy the modulo check, you need to figure out which one is actually a valid flag. Brute-forcing the scoreboard would be difficult because of the CAPTCHA. The value the wizard is looking for is the last 30 bits of the maze flag. Due to a typo when modifying the maze level to use the memory address from the oracle level, this value comes directly from the pre-computed table in the CRC32 algorithm. This made it possible to solve this level with another kind of brute force: simply checking every DWORD in the adventure binary, only one of which passes the modulo check.
Next we continue up the mountain.

> w
You are standing outside a yurt near the top of a mountain.
There is a path leading down to the north and up to the south.

Exits are north, south and east.
> s
At the top of the mountain you stand before a grand stone altar.

There is a necronomicon here.
Exit is north.
> look necronomicon
You see the book of the dead, bound in human flesh and inked in blood; this
ancient Sumerian text contains bizarre burial rights, funeral incantations,
and 128-bit hex numbers; it was never meant for the world of the living.
> talk necronomicon
Do you remember the words?
> talk necronomicon klaatu barada nikto
Segmentation fault (core dumped)

What happened here? Examining this function you’ll find that it executes “int 42h” which will cause it to crash on a normal Linux system. Here we need a hint from Aaron:

Trying this again in the JS Linux emulator:

> talk necronomicon klaatu barada nikto
Your vision blurs, stumbling to the ground you try to hold on. You fall
through the earth...

You find yourself under a huge dome of rock with light coming in through an
opening too high up to reach.

This flag is worth 300 points. Looking at the function in a disassembler shows that the input is compared against “klaatu barada nikto” but just before the strcmp call the code executes the “int 42h” instruction. To figure this out you had to examine JS Linux to find the code that we added:

if (0x42 == ga) {
    fa = xa[0] + 7;
    sb(118);
    fa += 1;
    sb(101);
    fa += 3;
    sb(116);
    fa += 4;
    sb(101);
    fa += 1;
    sb(99);
    fa += 1;
    sb(107);
    fa += 1;
    sb(116);
    fa += 1;
    sb(105);
    fa += 1;
    sb(101);
    fa += 1;
    sb(0);
}

This code transforms the string pointed to by eax from “klaatu barada nikto” to “klaatu verata necktie.” After the strcmp the string is then zeroed out so that it can’t be found in memory afterwards. This also means you only have one shot at speaking the right words.

> talk necronomicon klaatu verata necktie
The wind picks up and builds into a high-pitched scream. The book slams open,
pages flying, suddenly stopping on a blank page. Blood-red spots appear,
growing larger and joining until the entire page is covered, then fade away
leaving only: 23eb43f3071731e5c1a189aa8cdbfbc4.

Let’s see what we can find to the north.

> n
You are standing outside a yurt near the top of a mountain.
There is a path leading down to the north and up to the south.

Exits are north, south and east.
> n
You are on a long twisting mountain path.

Exits are north and south.
> n
You are standing outside. There is a cave to the east.

Exits are north, south and east.
> n
You stand in front of a tavern, the lights are on but it's eerily quiet.

Exits are south and west.
> w
In the center of the tavern are shelves of colorful bottles reaching to the
ceiling and an oval-shaped bar surrounding them. On the bar are 1024 shots of
some sort of moonshine forming a line completely encircling it.

There is a bartender here.
Exit is east.
> look bartender
You see a bartender named Josephus, but you can call him Joe.
> talk bartender
Would you like to play a little drinking game?
y/n? y
Josephus takes out a tiny flag on a tiny stick and places it in the glass in
front of you and says:
Let's call this shot number 7, you must start at shot number 1. You pick a
number between 0 and 1024 and that is how many glasses you skip before taking
the next shot. Once you take a shot that glass is removed from the bar. You
continue around the bar skipping and drinking, over and over, until you have
consumed all the drinks except for number 7, then you can take the flag.
What number do you choose? 0
You drink shot number 1.
You drink shot number 2.
You drink shot number 3.
You drink shot number 4.
You drink shot number 5.
You drink shot number 6.
Josephus snatches the flag from the shot in front of you and says:
You lose! There are 1018 shots left on the table. C'mon, Euler got this on his
first try.
Josephus sets up the game for the next player.
> talk bartender
Would you like to play a little drinking game?
y/n? y
Josephus takes out a tiny flag on a tiny stick and places it in the glass in
front of you and says:
Let's call this shot number 7, you must start at shot number 1. You pick a
number between 0 and 1024 and that is how many glasses you skip before taking
the next shot. Once you take a shot that glass is removed from the bar. You
continue around the bar skipping and drinking, over and over, until you have
consumed all the drinks except for number 7, then you can take the flag.
What number do you choose? -73
You drink shot number 952.
You drink shot number 883.
You drink shot number 842.
...
You drink shot number 604.
You drink shot number 717.
You drink shot number 393.
Victorious, you snatch the flag from the last glass. The flag reads:
a642940d0033a5fe920c275a6667c9e2.
Your head is spinning; you black out and hit the floor. Josephus loots your
corpse and sets up the game for the next player. You come to the next morning,
wondering where your pants are.

This flag is worth 250 points. This drinking game is better known as the Josephus problem. The strait-forward approach is to write a program to simulate the game and try every possible input. This will show that there is no solution in the range 0 to 1,024. However the bartender is vulnerable to a signedness bug. The input is compared against 1,024 as a signed integer and if it is less than or equal to 1,024 it is accepted. When it is used as a count for how many glasses to skip it is used as an unsigned integer, so an input of -73 is interpreted as 4,294,967,223. There are multiple negative solutions so to restrict it to a single solution, from which a unique flag is generated, the input is taken modulo 1,024. The bartender code is very inefficient so it cannot be used to find the negative solution directly by trying all possibilities.

The Map

The last two flags are hidden in the map.
CTF-Final-edge
One flag is simply 16 hex ascii digits appended to the end of the JPEG file worth 75 points. The other flag is a QR code worth 100 points.
The hint on the map,

bHFoczovL2Nlb2hrLmd6eC9LM2g1N1VhDQo=

, is base64-decoded to:

lqhs://ceohk.gzx/K3h57Ua

This is Vigenère-decoded with the key “ExodusIntelligence” to: http://imgur.com/C3b57Qn

Which is folding instructions for the map. The fold lines are faintly visible in the map. Once folded the QR code containing the flag appears. Some trimming of edges and color-enhancing with a sharpie, or GIMP, might be necessary.

CTF-map-folded

Finally

Congratulations to our winners!
Team DatNoobs took first place for $3,000 by completing all of the challenges first. Team StratumAuhuur took second place for $1,500 by being the second team to finish. No team took the third place prize of $750 before the competition ended.

We had a ton of fun organizing this competition for all of you adventurers. Thanks for playing and see you again next time!