The Space Heroes 2022 CTF was an online CTF from April 1st (4pm UTC) to April 3rd (9pm UTC) 2022. It was hosted by FITSEC and it even was their first time organizing such an event! Lots of applause to them for their hard work 👏 As a newbie CTF player, there were lots of challenges to have fun on and to successfully solve. This of course will improve my experience with CTFs and get me even more motivated to do more. The entire CTF was space themed, which made me even more motivated since I love space :D I’ve solved much more challenges compared to the Insomni’hack 2022 CTF, one reason is that there was more time; and also that some challenges were much more easier. This was an overall awesome CTF, especially for me as a newbie. I’ve learned a lot and hope to participate in their future CTFs!
The CTF ran another competition after the CTF ended where we were able to submit our write-ups and the best seven write-ups would get an ISS sticker signed by US Astronaut Colonel Andrew Morgan. I ended up being in the winners and got my sticker after some time:
So now let’s start the write-up - as always, all flags had the same format which was shctf{...}
. Let’s get right into the write-up:
k?
This was a warm up challenge. The description said:
“MEE6 was busted! Help us out and unlock the flag in #mee6…”
So here it’s pretty obvious, let’s head over to #mee6
on their Discord server and see what we can try out. When using the /help commands
commaannd, you can see a list of custom commands made by the Discord server administrators. There was a command that looked interesting: !k (optional text) - An awesome command!
.
When typing !k
you will get the flag sent in your private messages by the bot: k? shctf{WhY_iS_K_BaNnEd} 😭
. Free points for that one!
Discord
Another challenge on Discord, this time the flag was hidden somewhere in the Discord server. I’ve seen lots of people trying random things in the #mee6
channel to get that flag while it said somewhere. So I’ve used the search function to search for some flags; nothing. I looked at every channel topic and pinned messages; nothing alarming. But when I went back on the #mee6
channel, I clicked on MEE6’s profile; and there it was. MEE6 had a custom role named: shctf{4ut0b0ts_r013_0u7}
.
Guardians of the Galaxy
We are given a netcat connection and the binary of the program ( Download the file ). When testing the program locally it crashes, but why? Let’s investigate by opening the file in a disassembler.
This is the source of the crash, and it’s really easy to understand. If fopen
returns 0x0
, then the file doesn’t exist, and therefore the binary crashes. So let’s create a dummy flag.txt
file with the content FLAG_____FLAG
. But before running the binary again, we can see that the data for the file is stored at the location rbp-0x30
with a size of 0x20
.
To confirm that, we can run the file with gdb and check the content:
The binary prints exactly what we send with printf
according to this assembly code:
So let’s use some string formats such as %x
or others. When using %p
we get a nice hexadecimal representation of the address returned. So let’s print lots of them.
When looking at the data being given back, we can see some hexadecimal values of ASCII characters. Starting at 0x6d697b6674636873
and ending at 0x55f6d2000a7d
. So let’s write a Python script to extract that data:
"""
Useless data:
0x7ffd7417b6f0
0x55f6d353b2a0
0x2570257025702570
0x2570257025702570
0x2570257025702570
0x70257025702570
---------------
Important data:
0x6d697b6674636873
0x636172747369645f
0x756f795f676e6974
0x55f6d2000a7d
"""
from binascii import unhexlify
flag = b"".join(
[
unhexlify("6d697b6674636873")[::-1],
unhexlify("636172747369645f")[::-1],
unhexlify("756f795f676e6974")[::-1],
unhexlify("55f6d2000a7d")[::-1],
]
)
print(flag)
This gave back: b'shctf{im_distracting_you}\n\x00\xd2\xf6U'
, and there we have the flag, shctf{im_distracting_you}
.
Space traveler
We were given a URL: https://spaceheroes-web-explore.chals.io
. When going on the website we could hit the Guess The Flag
button. We had to give a flag as input and it would say if it’s valid or not. Looking at the network tab in the developers tool, not external requests were made. So the check is done locally. When looking at the source code there was some obfuscated source:
var _0xb645 = [
"\x47\x75\x65\x73\x73\x20\x54\x68\x65\x20\x46\x6C\x61\x67",
"\x73\x68\x63\x74\x66\x7B\x66\x6C\x61\x67\x7D",
"\x59\x6F\x75\x20\x67\x75\x65\x73\x73\x65\x64\x20\x72\x69\x67\x68\x74\x2E",
"\x73\x68\x63\x74\x66\x7B\x65\x69\x67\x68\x74\x79\x5F\x73\x65\x76\x65\x6E\x5F\x74\x68\x6F\x75\x73\x61\x6E\x64\x5F\x6D\x69\x6C\x6C\x69\x6F\x6E\x5F\x73\x75\x6E\x73\x7D",
"\x59\x6F\x75\x20\x67\x75\x65\x73\x73\x65\x64\x20\x77\x72\x6F\x6E\x67\x2E",
"\x69\x6E\x6E\x65\x72\x48\x54\x4D\x4C",
"\x64\x65\x6D\x6F",
"\x67\x65\x74\x45\x6C\x65\x6D\x65\x6E\x74\x42\x79\x49\x64",
];
function myFunction() {
let _0xb729x2;
let _0xb729x3 = prompt(_0xb645[0], _0xb645[1]);
switch (_0xb729x3) {
case _0xb645[3]:
_0xb729x2 = _0xb645[2];
break;
default:
_0xb729x2 = _0xb645[4];
}
document[_0xb645[7]](_0xb645[6])[_0xb645[5]] = _0xb729x2;
}
When looking at it, we can see it’s obfuscated, so let’s deobfuscate it!
function myFunction() {
let azante;
let karynna = prompt("Guess The Flag", "shctf{flag}");
switch (karynna) {
case "shctf{eighty_seven_thousand_million_suns}":
azante = "You guessed right.";
break;
default:
azante = "You guessed wrong.";
}
document.getElementById("demo").innerHTML = azante;
}
Well, that’s much more readable and we can see the flag in plaintext: shctf{eighty_seven_thousand_million_suns}
.
Curious?
This challenge was pretty straight forward, it was an OSINT challenge. For this one is was basically “Who can search better on Google?“. Well in my case, I used TinEye since we were given a picture:
When searching for that picture, there were some websites that had this picture. Here is a list of them:
http://www.dailytechinfo.org/tags/%C7%E0%E4%E5%F0%E6%EA%E0/ - Russian, nothing important for the challenge
https://dailytechinfo.org/space/5613-marsohod-curiosity-napolovinu-preodolel-voznikshee-pered-nim-prepyatstvie.html - Russian, nothing important for the challenge
http://news.discovery.com/space/the-moment-when-curiosity-breached-a-mars-dune-140205.htm - Offline
https://www.hjkc.de/_blog/2014/02/05/2391-mars-curiosity-chroniken---curiosity-news-sol-529-533/ - Could be interesting
https://www.space.com/24592-mars-rover-curiosity-dune-jump.html - Could be interesting
When looking at the last result, it clearly has as title “The Moment When Curiosity Breached a Mars Dune”. Considering the flag format was given, shctf{SOL_xxx}
, we can see that this picture was taken at SOL 533. So pretty simple, right? shctf{SOL_533}
is the flag.
The other website, hjkc.de also contained the SOL 533 picture, you just have to scroll a lot.
Launched
Another OSINT challenge, this time we are given the picture of a rocket that just launched.
Considering the flag format was shctf{rocket_payload}
, it’s pretty easy to know we need to find the rocket and its payload name. It’s also the first time I know payloads can have names 🤯
So let’s get exiftool in my hands. When looking at the data we got back, we can see some interesting information:
ExifTool Version Number : 12.40
File Name : launched.jpg
...
Date/Time Original : 2019:04:11 18:36:33
Create Date : 2019:04:11 18:36:33
...
One of them being the exact date and time when the picture was taken. So we can see that the rocket was launched at 2019:04:11 18:36:33
. Just need to find the rocket name and its payload now.
A simple Google search showed that the rocket was a Falcon Heavy. Now we need the payload name; let’s try Wikipedia. Yup, there we go:
So now let’s put everything in the flag format, shctf{rocket_payload}
, and we get shctf{falcon_heavy_arabsat-6A}
.
Flag in space
A web challenge. We are greeted with a website that has a grid with empty content. We URL was http://172.105.154.14/?flag=, so let’s try to put some garbage in the GET parameter. When trying some characters you can see that some grids now contain the character that was correct, so if you put the flag
parameter to shctf{aaa
, you get the following:
Looking at the source code it’s a basic <div>s</div>
for every character. We could try each character ourselves but some flags are known to have special characters or numbers so it would take ages. Therefore I made a simple script that appends every character, and if it gets the <div>
element, then it gets added in a variable res
. I’ve already put the known characters as an element in the res
variable.
Then we simply make a request with all the characters from res
and append the currently looped character. Here is my source code, you might understand it better:
import requests
flag = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "}"]
res = ["s", "h", "c", "t", "f", "{"]
for i in range(0, len(flag)):
for j in range (0x21, 0x7d):
char = chr(j)
url = "http://172.105.154.14/?flag=" + "".join(res) + char
r = requests.get(url)
response = "".join(r.text.split())
previous = ""
for k in range(0, len(res)):
previous += "<div>" + res[k] + "</div>"
if f"{previous}<div>{char}</div>" in response:
res += char
print("".join(res), end="\r")
break
print("".join(res))
After quite some time, the script gives the following flag back: shctf{2_explor3_fronti3r}
.
R2D2
Looking back at this one, it was pretty obvious… We are greeted on a website that looks like that:
Looking at the source code, nothing. Looking at the local storage, nothing. Looking at the cookies, nothing. I decided to run gobuster on the website with a wordlist. Guess what kind of file was detected… robots.txt, of course…
Getting on that file gives the flag back, shctf{th1s-aster0id-1<span>$</span>-n0t-3ntir3ly-stable}
.
Starman
Another OSINT challenge. This time the description says already a lot:
“How far away from earth was the space car on January, 20 2021 at 1515 UTC? Enter distance in terms of Million Km. (Rounded to two decimals) (e.g shctf12.34)”
Searching up on Google what the space car is, I came across this website. And at the right we can give a date and time, and we get an awesome map. When looking at the map we see that it was at 56.68
million km away from earth. Therefore the flag was: shctf{56.68}
.
Space Buds
“One of the puppies got into the web server. Can you help find out who it was?”
With that description there also was a picture of the Space Buds.
There also was a website, that pretty much contained nothing. But when inspecting the source code, there was a hidden input element.
So I searched up on the Internet what the names of the puppies were and put them one by one in the input field. But still nothing. However, when sending the form, there was a request made to /getcookie
. So maybe there’s something in my cookies?
But we can change the value of that cookie, so let’s put the name of each dog in the cookie and reload the page. When typing Mudbud, there was a flag given.
After literally decrypting that flag, it results to shctf{tastes_like_raspberries}
.
Cape Kennedy
This was a reversing challenge. We were given a Python file that contains a password check.
import sys
def main():
if len(sys.argv) != 2:
print("Invalid args")
return
password = sys.argv[1]
builder = 0
for c in password:
builder += ord(c)
if builder == 713 and len(password) == 8 and (ord(password[2]) == ord(password[5])):
if (ord(password[3]) == ord(password[4])) and ((ord(password[6])) == ord(password[7])):
print("correct")
else:
print("incorrect")
else:
print("incorrect")
if __name__ == "__main__":
main()
This one was quite easy to reverse. The password must have a length of 8. The characters and index 2 and 5 must be the same, the characters at index 3 and 4 must be the same and the characters and index 6 and 7 must be the same. The sum of the hexadecimal value of the characters must be 713. Let’s make a bruteforce script:
import random
from string import ascii_letters
pwds = []
def solve():
while True:
s = ["A"]*8
s[0] = random.choice(ascii_letters)
s[1] = random.choice(ascii_letters)
s[2] = random.choice(ascii_letters)
s[5] = s[2]
s[3] = random.choice(ascii_letters)
s[4] = s[3]
s[6] = random.choice(ascii_letters)
s[7] = s[6]
builder = 0
password = "".join(s)
for c in password:
builder += ord(c)
if (builder == 713 and len(password) == 8 and (ord(password[2]) == ord(password[5]))):
if (ord(password[3]) == ord(password[4])) and ((ord(password[6])) == ord(password[7])):
if password not in pwds:
pwds.append(password)
with open("moon.txt", "a") as f:
f.write(password + "\n")
solve()
And yes, there was A LOT of valid passwords, over 3 millions.
Considering it was space themed (It was mentioned in the description of the challenge again.), I searched what happened at Cape Kennedy and that was related to the moon, since the file was named moon.py
. It didn’t took long until I’ve found that Apollo 11 started from there, a historic moment in space exploration! Looking at the results in moon.txt
I’ve found a string generated that was APOllOaa
. So with the knowledge of before and that valid string, the flag is simply shctf{Apollo11}
.
Star Pcap
There was no description, just a pcap file ( Download the file ). When opening the file with Wireshark, we can see that there is just one slight change in all those ICMP packets, which is the ICMP code.
Using pyshark
it was easy to put all these codes together, convert them to an decimal value and then to a character.
import pyshark
capture = pyshark.FileCapture("star.pcap")
data = ""
for packet in capture:
data += chr(int(packet.layers[2]._all_fields["icmp.code"]))
print(data)
This resulted in the following string: c2hjdGZ7TDBnMWMtaSQtdGgzLWJlZ2lOTmluZy0wZi13aSRkb019
. Typical for CTFs, the data is base64 encoded. After decoding it, we can get the flag: shctf{L0g1c-i<span>$</span>-th3-begiNNing-0f-wi<span>$</span>doM}
.
Mysterious Broadcast
Another web challenge.
“There used to be 8 Models of humanoid cylon but now there are only 7. We’ve located one of their broadcast nodes but we can’t decode it. Are you able to decipher their technologies?”
When going on the website, there is a random ID generated in the URL, it looks like this: http://173.230.134.127/seq/710a1f63-57b9-4b86-a880-f413418375d9
The website had nothing besides an ~
as response, interesting. When reloading; it turned to a 1, when reloading again; it didn’t changed. But when reloading for the third time, it turned to a 0. Here is how it looked like:
Binary! But I don’t want to write everything down as it might be a lot of 0’s and 1’s in the end, so let’s make a quick Python script and save the output in a variable:
import requests
binary = ""
while True:
r = requests.get("http://173.230.134.127/seq/710a1f63-57b9-4b86-a880-f413418375d9")
if (r.text == "~"):
break
binary += r.text
print(binary)
print("\n[+] Received: " + binary)
# 1100011011001011010001101010110010010001111011010011011110100011011000100111011010101100001101011111011001001010110001101100001000101011001110100011101101110110001100001010101011001110100101101000110001011011011010010110100011000111101101101001001110011000011110011101111010111101
Here we go, we got the binary and now we can just decode it.
ÆËF¬í7£bv¬5öJÆÂ+:;v0ªÎ[ihǶyÞ½
ehhh, I don’t think that’s correct 🤔 Let’s look at the description again - There used to be *8* Models of humanoid cylon but now there are only *7*. [...]
. Remember, when representing a character, for example A, in binary there are 8 1’s or 0’s such as 01000001
. And according to the description, there are now only 7. So let’s put a space every 7th character and decode that.
Most of the decoders give the same output, as they don’t take in consideration the space. But this one did take in consideration the space. It resulted in c2hjdGZ7QXNjaWlJc0E3Qml0U3RhbmRhcmR9Cg==
. Again, the ==
is typical for base64 encoding. So let’s decode it: shctf{AsciiIsA7BitStandard}
.
Space Captain Garfield
This one was more about OSINT at the beginning. We have the following picture:
There was just the number 2254
not encrypted. So by searching garfield dreaming 2254
on Google I’ve found this picture:
So yes, what had to be done was to map each character to its sign and then reconstruct the flag in the last picture. I started and got shctf{lasa..alo.er}
, after some guessing for the last ones it was shctf{lasagnalover}
.
Netflix and CTF
This one was very similar to the Star Pcap challenge above. We are given a pcap file ( Download the file ) and we have to analyze it. When looking at it, there is always request made to http://10.10.100.124:8060/keypress/Lit_X
where X
always varies. Sometimes there is a request made to /browse
, this puts a line between the keypresses, and show names.
So let’s make a Python script using tshark again and save the output of each show in a list:
import pyshark
import urllib.parse
capture = pyshark.FileCapture("netflix-and-ctf.pcap")
data = []
show_data = ""
for packet in capture:
try:
if "browse" in packet.layers[3]._all_fields["http.response_for.uri"]:
data.append(show_data)
show_data = ""
continue
show_data += urllib.parse.unquote(packet.layers[3]._all_fields["http.response_for.uri"].replace("http://10.10.100.124:8060/keypress/Lit_", ""))
except:
pass
for show in data:
if "shctf" in show:
print(f"Special show found: {show}")
break
The special show found was: shctf{T1m3-is-th3-ultimat3-curr3Ncy}
.
Strange Traffic
My favorite forensics challenge. We were given again a pcap file ( Download the file ). There also was a free hint, so let’s take it:
“Hint: alt,esc,1,2,3,4,5,6,7,8,9,0,-,=,backspace,tab,q,w,…”
All right, now let’s investigate the pcap file.
The number encircled in purple is the only number that changes and appears in all packets. So we need to get this value for each packet. For this example, the 35
is formed thanks to the 33
and 35
which if decoded to in the ASCII table, they are 3
and 5
. Now with 35
what can we do? Let’s look at the hint again. It’s clearly a keyboard layout. Now if we look at the query keyboard layout that was sent on Discord:
If we start counting each key and count up to 35
, we get the key s
, which could fit for the s
in the shctf{...}
format. After checking with the other packets, this theory is right. Now let’s code a script for that:
import pyshark, binascii
capture = pyshark.FileCapture("strangetraffic.pcap")
# Map the entire keyboard
map = {
"1": "`",
"2": "1",
"3": "2",
"4": "3",
"5": "4",
"6": "5",
"7": "6",
"8": "7",
"9": "8",
"10": "9",
"11": "0",
"12": "-",
"13": "=",
"14": "<-",
"15": "tab",
"16": "q",
"17": "w",
"18": "e",
"19": "r",
"20": "t",
"21": "y",
"22": "u",
"23": "i",
"24": "o",
"25": "p",
"26": "[",
"27": "]",
"28": "enter",
"29": "caps",
"30": "a",
"31": "s",
"32": "d",
"33": "f",
"34": "g",
"35": "h",
"36": "j",
"37": "k",
"38": "l",
"39": ";",
"40": "'",
"41": "#",
"42": "shift",
"43": "\\",
"44": "z",
"45": "x",
"46": "c",
"47": "v",
"48": "b",
"49": "n",
"50": "m",
"51": ",",
"52": ".",
"53": "/",
"54": "ctrl",
"55": "win",
"56": "alt",
"57": "space",
"58": "alt",
"59": "win",
"60": "menu",
"61": "ctrl",
}
flag = []
for packet in capture:
payload = packet.layers[2]._all_fields["udp.payload"][75:].split(":") # Ignore the fist 75 characters from the payload
for i in range(0, len(payload)):
payload[i] = str(binascii.unhexlify(payload[i]), "ascii") # Coinvert to ASCII representation
flag.append("".join(payload))
res = ""
for f in flag:
res += map[str(f)]
print(res.replace("shift[", "{").replace("shift]", "}").replace("enter", "").replace("space", "_")) # Description said we can swap spaces with underscores
In the end we get the following flag: shctf{thanks_f0r_th3_t4nk._he_n3ver_get5_me_anyth1ng}
.
Future Stego
For this challenge there were two pictures, one to download which was:
There also was another picture in the description, as a hint:
After trying lots of steganography techniques, I couldn’t find any that lead me to the flag. One of the last was to use stegcracker
. I tried to bruteforce the password with the rockyou wordlist, but stopped at around 300’000 words tried. Nothing.
But the picture wasn’t here for nothing.. Let’s try some passwords that are in the news paper picture and use steghide --extract -sf shuttle.jpg
. After playing around and trying some combinations such as spacewoman
, newsweek
or sally k. ride
, I tried the file name: sallyride
. This was the password and extracted a text file which contained the flag: shctf{weightlessness_is_a_great_equalizer}
. It also would’ve worked with stegcracker
and the right password in a text file.