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.
hmmmm! #exodusadventure pic.twitter.com/BEjsLJeLSZ
— ɥʇɹıɯ (@sawall) April 11, 2015
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,
, encoded as 6 “magic words.” The wizard then expects a 30-bit number back from you,
, and computes
and checks that this value is equal to
. He then generates the flag from
. 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:
To those playing the #exodusadventure CTF, here's a hint: one of the challenges can only be solved with jslinux.
— Exodus Intelligence (@ExodusIntel) April 11, 2015
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.
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,
, is base64-decoded to:
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.
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!