Table of Contents
Hacky Holidays 2021 Challenges
Introduction
The Hacky Holidays “Space Race” 2021 CTF challenges ran between July 2 and July 26. I took part and finished in 64th place. Here is a quick overview of most of the challenges I completed:
su admin
This challenge consisted of the following flag image and a link to a flag designer. I was told to copy the flag to gain admin access.
The flag designer looked like this:
The flag designer had multiple choice options for generating a flag. Upon changing any of the options, a new flag image would be loaded from a remote URL where the path was influenced by your choices. Eg, it may be /1/2/3/4.svg if you chose those options. I used the web interface to copy the admin flag as best as I could but the image in the centre of the flag was missing from the options so couldn't be copied.
I solved the challenge by opening the flag image URL in a browser and manually changing the numbers in the path. I could see that most numbers were already correct because they were controlling the background colour and various shapes on the flag which I had already successfully copied manually with the web interface. One of the numbers controlled the overlay which I was missing and by using a number greater than what the UI allowed me to use, I gained access to the flag.
BowShock
“Bow shock is an amazing phenomenon, but you better not get too close… Can you find out how to minimize bow shock and prevent everything from turning into dust?”
This challenge included a .jar file which I uploaded to https://jdec.app which then showed me some easy-to-read code.
/* Decompiler 3ms, total 150ms, lines 50 */ import java.util.InputMismatchException; import java.util.Scanner; class BowShock { public static int totalInput; public static int getInput() { System.out.println("Set the amount of plasma to the correct amount to minimize bow shock: "); Scanner var0 = new Scanner(System.in); int var1; while(true) { try { var1 = var0.nextInt(); break; } catch (InputMismatchException var3) { System.out.print("Invalid input. Please reenter: "); var0.nextLine(); } } totalInput += var1; return var1; } public static void bowShock() { System.out.println("And all was dust in the wind."); System.exit(-99); } public static void main(String[] var0) { System.out.println("Oh damn, so much magnetosphere around here!"); if (getInput() != 333) { bowShock(); } System.out.println("We survive another day!"); if (getInput() != 942) { bowShock(); } if (getInput() != 142) { bowShock(); } System.out.println("Victory!"); System.out.println("CTF{bowsh0ckd_" + totalInput + "}"); } }
This code shows that the flag is CTF{bows0ckd_ appended with the value of totalInput. I could see that totalInput was being incremented on each execution of getInput() by the user-submitted value which this function requests. The function “main” shows getInput() being called 3 times and with the value the user is expected to enter (333, 942, 142). I added those 3 numbers together, appended to the flag string, and got the correct flag.
UFOria
Invite only
“Can you get a valid invite code? The flag is the invite code.”
The following JS validated the invite code:
function contactus() { var code = prompt("This option is invitation only. Enter your invite code:"); var verify = (function(code) { if (code.length != 12) { return false; } var parts = [code.substr(0,3), code.substr(4,4), code.substr(9,3)]; if (parts.join("-") != code) { return false; } if (parts[0] != "UFO") { return false; } if (parts[1] != btoa("UFO")) { return false; } if (parts[2] != ("UFO".charCodeAt(0) + "UFO".charCodeAt(1) + "UFO".charCodeAt(2))) { return false; } return true; })(code); if (verify) { alert("Great, please continue the booking process by sending us an email with your invitation code.") } else { alert("Wrong invite code.") } }
I could see that the user input would be split into 3 parts, the first part should be “UFO” followed by base64-encoded “UFO” (“VUZP”) followed by the sum of “U”,“F”,“O” character codes, all separated by dashes.
Members only
“Can you access the members-only area?”
The challenge webpage has a “forgotten password” page. I used the “about us” page to find a username belonging to an admin, then performed a password reset. The password reset form asked for my hometown as a security question. I looked on the LinkedIn profile of the owner of the username and found their hometown was Bourtange. The password reset page then just spat out the raw password “fataborgana42”. I attempted to log in with these credentials and was given “Login success! No member functionality implemented for now :) Have a flag instead: CTF{fataborgana42}”
Space Snacks
“Find the answers to the treasure hunt to gain access to the cake in the space cafe.”
Roten to the core
“You find a roten apple next to a piece of paper with 13 circles on and some text. What's the message?”
Vg nccrnef lbh unq jung vg gnxrf gb fbyir gur svefg pyhr Jryy Qbar fcnpr pnqrg pgs{Lbh_sbhaq_gur_ebg} Npprff pbqr cneg 1: QO
I recognised this as ROT13 and the “roten apple” was a good clue too. rot13.com is a decent website for converting it.
The roman space empire
“You find a page with a roman insignia at the top with some text what could it mean?”
Jhlzhy ulcly dhz clyf nvvk ha opkpun tlzzhnlz. jam{Aol_vul_aybl_zhshk} jvkl whya: NW
The references to romans immediately made me think of the Caesar Cipher so I used my go-to website for this kind of thing: https://www.dcode.fr/caesar-cipher which quickly gave me the answer.
The space station that rocked
“You hear the heavy base line of 64 speakers from the next compartment. you walk in and the song changes to writing's on the wall, there is some strange code painted on the wall what could it mean?”
RXZlbiAgaW4gc3BhY2Ugd2UgbGlrZSB0aGUgYnV0dGVyeSBiaXNjdXQgYmFzZS4gY3Rme0lfbGlrZV90aGVfYnV0dGVyeV9iaXNjdWl0X2Jhc2V9IC4gQWNjZXNzIHBhcnQgMzogWEQ=
I recognised this as base64 and decoded it with https://www.base64decode.org
What the beep is that?
“You hear beeps on the radio, maybe someone is trying to communicate? Flag format: CTF:XXXXXX”
.. -. ... .--. . -.-. - --- .-. / -- --- .-. ... . / .-- --- ..- .-.. -.. / -... . / .--. .-. --- ..- -.. / --- ..-. / -.-- --- ..- .-. / . ..-. ..-. --- .-. - ... .-.-.- / -.-. - ..-. ---... ... .--. .- -.-. . -.. .- ... .... ..--- ----- ..--- .---- / .- -.-. -.-. . ... ... / -.-. --- -.. . ---... / .--- --...
Morse code which I translated here: https://morsecode.world/international/translator.html
The container docker
“You are now in the space cafe, the cake is in the container that should not be here. You can see random names on all the containers. What will Docker never name a container? Note: Please enter it as ctf{full_name}”
I spent a while reading Docker documentation to determine if there are any reserved names but couldn't find anything which seemed like it could be the flag. Eventually I just googled “what will Docker never name a container” and found this page: https://medium.com/peptr/why-boring-wozniak-will-never-be-generated-as-a-container-name-in-docker-763b755f9e2a
There might be more cake
“They ate then cake and left a note with a secret algorithm to unlock the cake treasury. We saw it happening at exactly January 1, 2030 11:23:45 AM… are you the visionary that can figure out the PIN code? PIN code generation algorithm:”
int generatePin() { srand(time(0)); return rand(); }
In theory this challenge is quite easy. We need to do srand(x) where x is the epoch value for January 1, 2030 11:23:45AM and then return an integer with rand(). I used https://www.epochconverter.com to get the required epoch value (1893497025) and used this code to make my attempt:
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <time.h> #include <stdio.h> int generatePin() { time_t epoch = 1893497025; srand(epoch); return rand(); } int main() { int out = generatePin(); printf("The pin is %u \n" , out); }
I initially got the pin code wrong so I attempted to offset the epoch value by multiples of 3600 in both directions in case it was a time zone issue. Eventually I realised the problem was because I had compiled my code on MacOS instead of Linux. Once compiled on Linux, my code worked on the first try using 1893497025 as the epoch. I also mistakenly wrapped the pin in CTF{} for a few attempts which slowed me down even more.
Unidentifi3d Flying Object
“We've seen an Unidentifi3d Flying Object passing by. It was created on one of the universe's most advanced printers.”
Printer
“The UFO was forged on one of the universe's most advanced printers - do you know which make and model? Enter the answer as Make Model.”
This challenge consisted of a .gcode file. I simply looked through this file in a hex editor for a 3D printer model/make. I found one near the end of the file which I used for the flag.
Layer by Layer
“Do you know how GCode files work? Maybe you can find a hidden message along the layers. Flag format: CTF{answer}”
I used https://gcode.ws to “view” the file. I used the 2D viewer and adjusted the view so I could scroll through various layers. Eventually some text became visible which was the flag.
Rover
Gallery
“Our gallery with Rover images is beautiful - we had a lot more available, but we had to make a selection of which ones we added to the public gallery.”
This challenge page consisted of a gallery page which featured multiple images, the hint indicates we are looking for an image not included in the HTML. Each image had a URL like this: “/Home/Image?file=2.png” where each image had a different number. I manually checked for additional images by increasing the number in the URL bar until I found a “hidden” image at “/Home/Image?file=22.png”.
Revving
“Do you hear the motor of the rover revving?”
During the first challenge, I suspected an LFI (local file inclusion) vulnerability and confirmed it by visiting “/Home/Image?file=/etc/issue”. I did some research into “revving” so I could figure out what the clue was telling me and I found this page which documents an LFI vulnerability in a WordPress plugin: https://blog.sucuri.net/2014/09/slider-revolution-plugin-critical-vulnerability-being-exploited.html
I had also discovered this web application was running on .net due to the .net anti-CSRF cookies being set on the “subscribe” page which was an important piece of the puzzle. This Microsoft Docs page indicates that a .dll file will be created named after the project. Using this information, I was able to download a file called “Rover.dll” which I then decompiled using ILSpy which was able to show me a flag which was included in the binary.
Fanmail
“A very famous person has subscribed to our gallery updates. Can you find out their mail address so we can send some fan mail?”
The subscribe page accepts an email address but is vulnerable to SQL injection. As shown in the decompiled DLL, email address validation is being performed server side meaning that only addresses which are valid can be passed to the SQL server.
I solved this challenge by visiting /Home/Image?file=/var/lib/mysql/ibdata1 which gave me the MySQL server data file; I used “strings” to easily find the email address.
Subliminal advertising
“The agency who created the space ship has included some subliminal advertising. Can you find out what their secret message is? Flag format: CTF{hex}”
This challenge consisted of a large animated png. The default “preview” application on MacOS splits out each frame so they can all be inspected manually. I noticed a flag written in a tiny font on a single frame; it took a while to correctly read because of the font they used.
Mission Control
Authenticate
“Can you authenticate?”
This challenge consisted of a web page asking the user to log in. A drop down box was included for the “Realm”; when looking at the HTML it became clear this dropdown box was simply changing an IP address which was being sent to the server. I changed the IP to something random and received the message “Can't contact LDAP server” when attempting to login.
To solve this challenge, I spun up a new DigitalOcean VPS running Ubuntu 20 and installed an LDAP server like so:
apt update apt install slapd ldap-utils
I submitted the form again but with my DigitalOcean IP address and saw the following packet when running tcpdump on the VPS:
0x0000: 261a 914e 034f fe00 0000 0101 0800 4500 &..N.O........E. 0x0010: 0063 d7f2 4000 3806 c105 88f3 444d 867a [email protected] 0x0020: 55e2 8990 0185 ae96 5f59 9bb3 e34e 8018 U......._Y...N.. 0x0030: 01f6 c165 0000 0101 080a ecc3 89a0 081b ...e............ 0x0040: a785 302d 0201 0160 2802 0103 0419 636e ..0-...`(.....cn 0x0050: 3d61 646d 696e 2c64 633d 7370 6163 652c =admin,dc=space, 0x0060: 6463 3d63 6f72 7080 0870 6173 7377 6f72 dc=corp..passwor 0x0070: 64 d
The following part is important: cn=admin,dc=space,dc=corp
I then ran dpkg-reconfigure slapd which allowed me to configure a domain and a password.
I submitted the form again and was given a flag. I used “admin” and “password” for the credentials and used “space.corp” as the domain.
Stolen Research
Kernel release
“What sort of OS and kernel is the actor using? Give us the kernel release version (the output of the 'uname -r' command).”
I ran “strings” on the memdump and grepped for “Linux”. I found this line:
Operating System = Linux version 5.10.0-kali8-amd64 running on amd64
The flag was “5.10.0-kali8-amd64”.
Password of the actor
“What is the password of the actor?”
I used strings and grep again in an attempt to find the shadow file. I found this quite quickly:
invictus:$y$j9T$i6GkFortXamhKHY0bpTN.0$FLCqzsvVB1ZnfpffqSuvdLgzwLJvkmz6.aHfyoo11NB:18808:0:99999:7:::
The flag is the unhashed password and the “$y$” part indicates yescrypt is in use which means less software supports brute forcing it. Eventually I discovered how to tell John The Ripper to brute force scrypt (–format=crypt) and got the password.
Password of the share
“The actor compromised sensitive credentials of the research centre and used them to authenticate to a network share. What is the password of the network share they logged on to?”
I knew the IP address of the network share thanks to the pcap which this challenge included. I used strings and grep to find references to it (using the -C flag so I could also see surrounding lines) and eventually found “Administrator” was the network share username. I then grepped for “Administrator” with 50 surrounding lines shown and then grepped that for “password”. I found a block of text which included the share IP, the username, “org.gnome.keyring.NetworkPassword”, and “JUPITER” which I initially thought was the password.
I then went to this section in a hex editor so I could look around. I found “Shuttle9812983” a few lines up which was the password.
Skylark Capsule
I completed the challenge “Skylark Capsule” and made a separate page for my writeup. This page was used as an entry into the “best writeup” challenge but did not win.
My writeup can be accessed here: Skylark Capsule CTF Challenge