By spareus comprising Liu Jie Xu (@8061xjl) and Liu Weichu (@dabby9734)
🚧 WRITEUPS ARE INCOMPLETE AND STILL WORK IN PROGRESS 🚧
Archive of CTF challenges can be found here (my manually archived version lol) or here (official).
Writeups are limited.
- Shalom Shalom
- Turbo Fast Crypto, part 1
- I can't open this file? Part 2
- Diffie's Key Exchange
- I can't open this file? Part 1
- totallyfoolproofcrypto
- Diffie's Key Exchange 2
- whodunnit
- "The Sieberr" Heist Part 1
- A Wealth of Information Part 1
- We go way back
- A Wealth of Information Part 2
- "The Sieberr" Heist Part 3
- "The Sieberr" Heist Part 2
- Public Transport Hunt
- [Part 1] FUTURE TECHNOLOGIES AI IOT FOURTH INDUSTRIAL REVOLUTION SECURITY CAMERA
- [Part 2] FUTURE TECHNOLOGIES AI IOT FOURTH INDUSTRIAL REVOLUTION SECURITY CAMERA
- TaiYang IT Solution Part 1
- Exploring The Universe! (Part 1)
- TaiYang IT Solution Part 2: Electric Boogaloo
- Duck Delivery
- Birds?
- Digging In The Dump Pt. I
- Digging In The Dump Pt. II
- Mind Cracking Adversity
- Exploring The Universe! (Part 2)
- plush, lush, flush, blush
- Heads and Tails Part 1
- Heads and Tails Part 2
- Heads and Tails Part 3
- Can You Math It?
- I lost my anime collection! Pt. II
- I lost my anime collection! Pt. I
- rock farming simulator deluxe edition
We did not solve this challenge during the competition.
Summary of how this works:
This is not a complete writeup, just an extension of what I had to do to get it to work.
Working examples from challenge author main:
RFS.mov
The solution is to quickly buy 2 ponies during the delay. However, it was incredibly difficult for me to get that to work (perhaps I'm just too slow lol), so I wrote a small AHK script to send the keys I need, hoping it would be faster:
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
KeyWait, LAlt, D
KeyWait, LAlt, L
Send b
Send t
Send b
Send b
Send p
Send h
Send h
It worked. scripts > humans
This challenge provided us with a string to decrypt (xibkgltizksbrhxllo
). The words AT and BASH were capitalised in the challenge description, indicating a clue.
Intended solution was to decipher the string with the Atbash cipher that the clues were referring to. By deciphering it, we get the flag cryptographyiscool
We ran the string through a cipher identifier to find out what cipher was used.
We get the flag by deciphering the string with Affine Cipher in bruteforce mode. It is interesting to note that the Atbash cipher is just an Affine cipher with a fixed key 🙂.
After staring at the challenge for a while, we discover that understanding math is important. We notice if priv-key
, which is an input we control, is 0
, B
will be 1
(because math), and consequently shared_secret
will also be 1
. This allows us to get the flag as a long integer without any modification, which we can then convert using long_to_bytes()
to get the flag.
Analysing the encryption method, we can see that after the flag is Base64 encoded, it is converted to a bunch of (zero-padded) double digit integers that represents the position of each character in the digits
list. Therefore, we split the encrypted flag into groups of two digits, and then convert the integers to characters using the digits
list, giving us SVJTe24wd195MHVfYzRuX2MwZDN9
. We then Base64 decode to get the flag.
Opening the webpage given, we see an form we can check the flag with.
Opening developer tools, we see that the Check
button runs a javascript function.
function check_flag() {
let flag = document.getElementById('flag');
let result = document.getElementById('result');
clearTimeout(hide);
result.textContent = btoa(flag.value) === "SVJTe2luc3AzY3RfZTFlbWVudH0=" ?
'Correct!' : 'Wrong.';
hide = setTimeout(() => { result.textContent = ''; }, 500);
}
The user input is being encoded with Base64 and compared to the similarly encoded flag.
By decoding the flag with a base64 decoder, we get the flag (IRS{insp3ct_e1ement}
).
We modify the script slightly to accept a list of numbers so we can investigate the transformations done by the script.
print("Enter the flag and I will check it for you.")
- enteredFlag = input()
+ enteredFlag = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]
coolarray = [[[], [], []], [[], [], []], [[], [], []]]
if len(enteredFlag) == 27:
for i in range(3):
for j in range(3):
for k in range(3):
print(i, j, k)
coolarray[i][j].append(enteredFlag[i*9 + j*3 + k])
newflag = ""
for i in [coolarray[2], coolarray[0], coolarray[1]]:
for j in i[::-1]:
for k in [j[1], j[2], j[0]]:
+ newflag = newflag + "," + str(k) # convert int to str
+ newflag = newflag[1:] # remove the first comma
print(newflag)
if newflag == "1}c5f1bbeb4580{RSI43db46731":
print("Your flag is correct!")
else:
print("Your flag is incorrect. :(")
else:
print("Your flag is incorrect. :(")
This generates the following output:
26,27,25,23,24,22,20,21,19,8,9,7,5,6,4,2,3,1,17,18,16,14,15,13,11,12,10
We can then write a script to reverse the transformation which yields the flag! (IRS{805b41736b4d43ebb15fc1}
)
# script to reverse the transformation
newflag = [26, 27, 25, 23, 24, 22, 20, 21, 19,
8, 9, 7, 5, 6, 4, 2, 3, 1,
17, 18, 16, 14, 15, 13, 11, 12, 10] # data to test with
newflag = list("1}c5f1bbeb4580{RSI43db46731") # transformed flag
# reassemble array in correct order
hyper_big_chunkus = []
for i in range(3):
big_chunkus = []
for j in range(3):
chunk = [newflag[i*9 + j*3 + 2],
newflag[i*9 + j*3],
newflag[i*9 + j*3 + 1]]
big_chunkus.append(chunk)
big_chunkus = big_chunkus[::-1]
hyper_big_chunkus.append(big_chunkus)
hyper_big_chunkus = [hyper_big_chunkus[1],
hyper_big_chunkus[2],
hyper_big_chunkus[0]]
# convert output array to string
nice_flag_bro = ""
for i in range(3):
for j in range(3):
for k in range(3):
nice_flag_bro += hyper_big_chunkus[i][j][k]
print(nice_flag_bro)
By viewing the exif data of the image, we obtain the location where the image was taken (33°49'27.1"S 151°15'01.0"E).
From the satellite view, we get the street name (HUNTERRD) and the postal code (2088). A quick google search of the localities in the Mosman suburb yields the locality (BALMORAL).
From the street view, we get the unit number (3) and number of stories (2) of the building.
Assembling our information, we get the flag. (IRS{3_HUNTERRD_BALMORAL_2088_2}
)
We find the individual "Casrihms Myrert" on Facebook.
On his facebook post, we see that he is travelling from the Sydney CBD to a hospital somewhere in Northern Beaches. He also shares that this bus is quite direct.
A look at the comments section also reveals that the hospital is not Northern Beaches Hospital. It is also rather sad that someone asked him to go die 😭.
By searching for the hospitals near Northern Beaches, we find that Mona Vale Hospital has a direct bus (Route B1) from the Sydney CBD to it.
We can then obtain the destination station of route B1 (MONAVALE) from Moovit.
Assembling our information, we get the flag. (IRS{B1_MONAVALE_MONAVALEHOSPITAL}
)
From the given image, we get the bus route number (340). Knowing the route, we can find the destination station on Moovit (BONDIJUNCTION). We also notice that the bus is making a left turn at a T-intersection.
By following the route for bus 340, we find that it makes a left turn at a T-intersection after Wynyard Station. We plot the route using a bicycle path here as bus 340 is no longer in service and the driving route generated made no sense.
By zooming in with street view, we can confirm we have the correct location.
We refer back to the satellite view, and we get the name of the street that the photo was taken on (YORKST) and the name of the cross street (DRUITTST).
We go back to the given image, which shows us the bus is gives us the bus fleet number (2213). This allows us to look up the brand (VOLVO) and model (B12BLEA) of the bus on the State Transport Authority's website.
Assembling our information, we get the flag. (IRS{YORKST_DRUITTST_VOLVO_B12BLEA_BONDIJUNCTION}
)
Notice that the file is rather large (around 12 mb) while the image itself is small. Playing around with the image (and deducible from the hint), we can extract the zip file embedded in the image using binwalk. The flag appears for a short while in the gif.
From experiences with other CTFs (also mentioned in the hint), we put the audio file into Audacity (other common software used for this purpose include Sonic Visualiser) and notice a very odd section near the middle when viewing the waveform:
Looking at the spectrogram, we can see some text in that region.
Zooming in, we get some hex: 68 74 74 70 73 3a 2f 2f 70 61 73 74 65 62 69 6e 2e 63 6f 6d 2f 5a 7a 6b 61 46 59 69 4c
, which when decoded gives us a pastebin containing the flag.
Using DB4S (or some online SQLite viewer), we can take a look at Alex's Chrome history (stored in the SQLite database AppData/Local/Google/Chrome/User Data/Default/History
). In the urls
table, we see the last two URLs leads us to http://challs.sieberrsec.tech:23547/dcfa237943d4fd7e2a514ca54642efaccd2cdbd5003bfb19a1e70737273e1190/ where the flag is displayed:
We can easily get the username from the saved logins (stored in the logins
table in the SQLite database AppData/Local/Google/Chrome/User Data/Default/Login Data
), but the password is slightly more difficult to get since it is encrypted.
Intended solution using ChromePass is less tedious than what we did. There was also a unintended solution by a participant who found the flag in cached in Chrome (there's an useful tool for that as well).
We found a script, but it uses the local machine's DPAPI functions, which we do not want. Therefore, we exported the "master_key" before the CryptUnprotectData function was called.
Using Mimikatz, we extracted the DPAPI master key with the user password given to us following this guide (dpapi::masterkey /in:"AppData\Roaming\Microsoft\Protect\S-1-5-21-1937579505-2679969469-2152769792-1001\37b49573-c2de-487a-81be-b8c2f9b4df15" /password:Password1 /protected
). We can then use this masterkey to decrypt the exported master_key from the script (dpapi::blob /in:masterkey /unprotect
, specifying the /masterkey: is unnecessary since Mimikatz keeps the DPAPI keys cached in the same session).
After decrypting the master_key blob, we can put it back into the script and run it to extract the password.
Getting the username (Alex24
) and password (IHeartCookies
), we log into the website from the previous part of this challenge series, and we get the flag:
Add the given disk as a data source into Autopsy. We find a video file (Kimi No Na Wa Clips.mp4
) where the flag is displayed.
We did not solve this challenge during the competition.
Create a Windows Server virtual machine (evaluation ISOs available at here), since "A consumer version of Windows can't read RAID 5!" and we will have to use a Windows Server OS. After mounting the two disks onto the VM and creating a new empty disk, we can use Disk Management to rebuild the RAID5 array (right click on disks > Import Foreign Disks..., then right click the RAID5 volume > Repair Volume...). We then assign the volume a drive letter and simply open the video stored in it (Tenki No Ko Clips.mp4
) to see the flag.