hoifanrd’s osu! Challenge
This is a beginning of a new series of blog posts that I review CTF challenges those are not publicly accessible. Generally I will spend some time to solve the challenge while discussing with the author, and give some feedback of the challenge.
To kick-start, let's look at hoifanrd from Firebird CTF team has shared a challenge he created some days ago.
Background⌗
At around 11pm of January 25, I received a direct message from hoifanrd that he is going to make a guessy challenge. I replied "make it" in few minutes. On the next morning, I saw a challenge came alive in the Discord server of the Firebird CTF team.
Challenge Summary⌗
This is the challenge description he left in the Discord server:
The journey starts here...
P2k9ZmhuM2U5U2pkeDA=
On top of that, he also commented that the challenge would be fun since it involves some abnormal stego and hiding techniques.
Solution⌗
Part I: An obvious base64⌗
Instead of the above base64-encoded string, I started with the challenge description he DMed me (as below). At that time I was not awared of the description he sent to the server is different from what he sent to me.
The journey starts here...
ZmhuM2U5U2pkeDA=
Noticing that the string being encoded in base64 is not hard. Decoding it, I have fhn3e9Sjdx0
. Knowing that being an osu!-related challenge, I thought this is an username he created in osu!. Turns out it isn't. I honestly had no clues and searched osu fhn3e9Sjdx0
on Google. It returns only one entry - A YouTube video recording the osu! gameplay of firebat92. Well it is very likely to be unrelated. I don't even know the reason it came to my search result, since the second keyword isn't found anywhere. In particular, the video ID is jbTebNnwarg
, which is not what I expected.
Well... I was messing around and tried to plug fhn3e9Sjdx0
as the video ID. Video unavailable. However, one thing I learnt is that the video ID of YouTube is 11 characters long... Coincidence? Later I was reminded by hoifanrd that the challenge description I received is not up-to-date. For the updated one, it is ?i=fhn3e9Sjdx0
decoded. I am more convinced that it is a YouTube video. Let's figure out how to convert the i
into v
?
The first thing I think is Caesar cipher. I bet it wouldn't be Vigenere cipher owing to its short message, and I would definitely question the author's knowledge on computer science if it is Vigenere cipher. I threw the ciphertext to cryptii.com and eventually found ?v=sua3r9Fwqk0
...
⏰ Time elapsed: 10 minutes.
Part II: Figuring out that being a .wav
stego...⌗
hoifanrd played two beatmaps in the video. They are
- Hyakka Ryouran Hanafubuki [Iceluin's Hard] mapped by Hojo Karen (0:00-3:47), and
- Koyoi Tsuki no Yakata Nite (Cover) [Hard] mapped by hoifanrd (3:55-7:43).
Knowing that being a stego challenge, it must be the second beatmap that the secret is hidden. At first, I wanted to see if something inside the video. I was soon stopped by hoifanrd and pointed that I am not on the right track. Well, since we can download the beatmap from osu.ppy.sh, let's download the beatmap and analyse. It is an .osz
file that can be opened like zip file.
I suspect that there is only one file with the secret hidden. I could immediately eliminate five files because they are modified earlier than Jan 25, the day he told me that he had an idea. I quickly looked into the three .osu
files and didn't find anything fishy. After all, it is very likely to be a .wav
stego.
Okay, so what I have to do is to find both the passphrase and the correct tool hiding the secret bits.
⏰ Time elapsed: 75 minutes (+ 65 minutes).
Part III: Finding the passphrase⌗
After a long break (i.e. my working hours), I am back to the challenge. Without the passphrase, I am unable to tell if the tool is correct. Hence I decided to find the passphrase first. Knowing that there are two beatmaps in the video those serve their own purpose, I was focusing on the first gameplay. If the second gameplay contains the secret, then the key must be hidden in the first one.
Well... I made no progress for an hour and at some point of time I decided to stream on the Discord server. Eventually hoifanrd thinks I am too weak and decided to give me some hints:
It should be something that is easy to aware... notice the cursor
After a short while, I noticed a cursor having a heartbeat behaviour on 2:46, the cursor was not pumping on the other timestamps. It must be morse code.
However there is no significant time difference between a dash and a dot. What can we do to distinguish dashes from dots? Well, if you met some conditions, then the replay will be recorded and uploaded to osu.ppy.sh (as an .osr
file). The first gameplay satisfies such conditions and is available online. There are multiple approaches to read an .osr
file, since I am lazy to install osu! for that, I found a Python package on Github called osrparse. Doing some math, I identified the time interval of the heartbeat and extracted the key pressed during the interval.
The above figure marks the keys pressed during the interval:
- A blue dot is the place he pressed M1,
- a red dot is the place he pressed M2, and
- a green dot is the place he pressed M1 and M2.
After all, we have .-- . .-.. --- ...- . ... .- --
, which decodes into WELOVESAM
.
⏰ Time elapsed: 200 minutes (+ 135 minutes).
Part IV: The final bits⌗
The only thing remaining is to find the correct tool to extract the secret hidden in the .wav
file. Since steghide is the most common one amongst them, I am able to extract the secret with the following command.
steghide extract -sf audio.wav -p WELOVESAM
I have extracted the message below. Well, I quickly realized that is nonogram.
7 2 2 1 1 7
1 1 2 1 1 1
1 3 1 1 6 2 1 3 1
1 3 1 2 1 3 1 1 3 1
1 3 1 4 3 2 1 3
1
1 1 1 1 1 1 2 1 1
7 1 1 1 1 1 1 1 7
1 1 2 2 1
4 1 1 4 1 2 3 1
1 2 2 1 3 2
1
1 1 1 1 3 2 1
1 1 5 4 2
1 5 1 1 2 1 3
1 1 5 1 2 1 1 1 1
3 1 3 1 1 2 1 1 3
2 1
1 1 2 3 4 1
1 3 1 1 1 3 5
1 1 1 2 2 1 2 1
1 6 3 1 3
3 4 1 4 1 1 1
1 1 2 1 3 8
3
1 1 3 5 1 2
7 1 3 1 3 1 2 1
1 1 1 2 1 2
1 3 1 1 1 6
1 3 1 2 2 3 2 1 2 1
1 3
1 4 1 2 1 1 1
1 1 4 2 1 1 2 1
7 3 1 1 1 3 1 1
7 1 1 1 7
1 1 2 2 1 1 1 1 1
1
3 1 1 1 1 1 1 3 1
1 3 1 1 4 3 1 3 1
1 3 1 1 1 1 1 2 1 3 1
1 1 1 2 1 1 1 1
7 1
1 1 1 1 1 1 7
3 1 3
1 3 1 2 2 1 2 1 4
1 3 1 1 3 2 1 4
1 1 3 3 1 3
3 1 3 1 1 1
1 3
1 2 1 2 6 2 1
1 1 1 2 1 1 1 1 3 1
1 1 5 2 1 1 2 2
3 1 1 3 1 1 1
4 5 2 2 1
2 1 1
1 1 1 1 1 2 3 1
1 1 1 1 2 4 4 2
1 1 2 1 1 4 4 1 2
1 1 2 4 3 5 1 1
1 2 1
1 1 2 1
7 1 2 1 1 1 1
1 1 1 2 1 1 2 1
1 3 1 3 3 1 6 2
1 3 1 3 1 2 1 1 2 1
1 3
1 1 1 1 1 3 1
1 1 4 3 4 2
7 2 1 2 1 3 1 2
I like playing puzzle...
Unfortunately, hoifanrd noticed that the message isn't correct owing to some issues regarding to line breaks. I have received an updated message from him:
7 2 2 1 1 7
1 1 2 1 1 1
1 3 1 1 6 2 1 3 1
1 3 1 2 1 3 1 1 3 1
1 3 1 4 3 2 1 3 1
1 1 1 1 1 1 2 1 1
7 1 1 1 1 1 1 1 7
1 1 2 2 1
4 1 1 4 1 2 3 1
1 2 2 1 3 2
1 1 1 1 1 3 2 1
1 1 5 4 2
1 5 1 1 2 1 3
1 1 5 1 2 1 1 1 1
3 1 3 1 1 2 1 1 3
2 1 1 1 2 3 4 1
1 3 1 1 1 3 5
1 1 1 2 2 1 2 1
1 6 3 1 3
3 4 1 4 1 1 1
1 1 2 1 3 8 3
1 1 3 5 1 2
7 1 3 1 3 1 2 1
1 1 1 2 1 2
1 3 1 1 1 6
1 3 1 2 2 3 2 1 2 1
1 3 1 4 1 2 1 1 1
1 1 4 2 1 1 2 1
7 3 1 1 1 3 1 1
7 1 1 1 7
1 1 2 2 1 1 1 1 1
1 3 1 1 1 1 1 1 3 1
1 3 1 1 4 3 1 3 1
1 3 1 1 1 1 1 2 1 3 1
1 1 1 2 1 1 1 1
7 1 1 1 1 1 1 1 7
3 1 3
1 3 1 2 2 1 2 1 4
1 3 1 1 3 2 1 4
1 1 3 3 1 3
3 1 3 1 1 1 1 3
1 2 1 2 6 2 1
1 1 1 2 1 1 1 1 3 1
1 1 5 2 1 1 2 2
3 1 1 3 1 1 1
4 5 2 2 1 2 1 1
1 1 1 1 1 2 3 1
1 1 1 1 2 4 4 2
1 1 2 1 1 4 4 1 2
1 1 2 4 3 5 1 1
1 2 1 1 1 2 1
7 1 2 1 1 1 1
1 1 1 2 1 1 2 1
1 3 1 3 3 1 6 2
1 3 1 3 1 2 1 1 2 1
1 3 1 1 1 1 1 3 1
1 1 4 3 4 2
7 2 1 2 1 3 1 2
I like playing puzzle...
I have imported the nonogram to an online nonogram solver. The solver occasionally stop working owing to unknown reasons (maybe the solution is not unique), so I decided to guess the shaded bits. Luckily I am streaming this, so that the ones who are watching can also deal with it while I am stuck.
Shortly after, we have enough information for the QR code to be scanned correctly: flag{W3_l0v3_S4m_4nd_p1z_dun_1ov3_m3}
. Sam is blushed.
⏰ Time elapsed: 220 minutes (+ 20 minutes).
My Feedback⌗
In short, there are a few steps to get it through.
- Decode with base64 and decrypt with Caesar cipher for the video link
- Find the file that conceals the secret something
- Find the passphrase to open the secret
- Find the steganographic tool to open the secret
- Solve the puzzle from the secret for the flag
The challenge is not difficult itself, but I am very sure that I am unable to solve it on my own. Parts 1 and 5 are okay. I would think part 4 is guessy if it is not steghide. However, parts 2 and 3 are the toughest parts. They are the most guessy, but at the same time I learnt something interesting.
To solve parts 2 and 3, you need to know what hoifanrd thinks (or wait until he gives hints). There are two beatmaps, and the reason why it got to be two is:
- The replay will be uploaded only if the gameplay is one of the top 1000 games in a given ranked beatmap. The first beatmap (Hyakka Ryouran Hanafubuki) satisfies the condition, hence you are able to download the replay from osu.ppy.sh.
- Needless to say, you can hide steganographically the
.wav
file in your own beatmaps.
Apart from this, you do need good eyesight to solve the challenge. Otherwise it will be impossible to notice the morse code during the break time... It is good to learn parsing .osr
files and liked the idea of hiding something in such a way.