CTF Circle - Hack-A-Sat 2023 Qualifier CTF "Dashing" Challenge Writeup Written by sen (@sen@hachyderm.io) ############### ### Summary ### The 'dashing' challenge provided a file named 'beepboop.wav' and a note that you have to investigate the odd-looking wav file to discover a hidden message. ############################ ### Tools/Infrastructure ### I used Adobe Audition to pre-process the audio, and a little bit of Ruby to do the last bit of the decoding. I'm sure Audacity would have worked too, but I've been using Audition since back when it was Cool Edit Pro and typically spend a couple hours a week in it, so it's my go-to tool when I need to do something with audio quickly. ########################### ### Phase 1 - Discovery ### To start off, I just played the wav file in a normal audio player. It just sounded like static with nothing else standing out, which was pretty much what I'd expected. I threw it into Adobe Audition and opened the Spectral Frequency Display to see what might be hidden in there. As expected given the name, there was a band around 200Hz that stood out, which looked like morse code once I zoomed in on it. #################################### ### Phase 2 - Automated Decoding ### I did a bit of cleanup of the signal using Adobe Audition, using the "FFT Filter" to do a sharp bandpass notch around 200Hz where the signal seemed to be. The audio was still hard to interpret, but I'd hoped the computer would be better than me at interpreting it. I figured that given the use of morse code on amateur radio, combined with the fact that there's often very poor signal-to-noise ratios on the CW bands, that there must be some tool that could decode the morse code automatically faster than I'd be able to do it. I looked around and found mostly tools that looked like they hadn't been updated since the Windows 3.1 days, and in a couple cases they mentioned they required Windows 95. I settled on trying fldigi which is currently maintained, but after installing it and playing with it for a few minutes I wasn't able to get anything useful out of it. I could get a few accurate characters here and there, but the amount of noise still present made it not accurate enough to be worthwhile, I'd spend more time cleaning it up than I would just doing it by hand in the first place. I searched the web to see if I could come up with anything else, and found the Morse Code Adaptive Decoder (https://morsecode.world/international/decoder/audio-decoder-adaptive.html). This seemed promising, but I was still getting random "e" characters from the noise ("e" being just a single dit), so I was ending up with strings like 6 e e e 7 6 e 4 e e. Not knowing what the encoding was, I didn't know if I'd be able to strip the random "e"s out safely after so this wasn't useful. I played around with the settings for awhile longer, then gave up on this approach. As a side note, someone else mentioned in the post-game that they used this tool successfully, so with some more tweaking of settings I might have got there! But for all I knew at the time, I could have played with it for another hour and ended up nowhere. ######################################## ### Phase 2 - Manual Decoding Plan A ### I decided that the automated systems weren't going to work, so considered what manual options there were. I thought about it for a few minutes and decided to try listening to the audio (processed with the filter to remove as much of the noise as I could get) and enter it into an iambic keyer app as I listened, pressing one button for dit and one for dah. It turned out to be harder than I'd expected to find such an app though, with the first one I tried not having been updated in so long that it didn't properly support my current phone, and the second one crashing every ten characters or so. I took a brief break to hang out with my dog, and realized that there was no reason I had to enter it as code. I could just type out dots and dashes into a notepad, and easily decode it later from there once I had it in text form. ######################################## ### Phase 2 - Manual Decoding Plan B ### I opened a notepad, and started playing the audio. Pretty quickly I realized that I wasn't going to be able to decode it by ear, even with most of the noise removed from the other frequencies the noise present around the same frequency still made it very hard to identify the characters. As a side-note, I'm hard of hearing and visually impaired, which makes this task a bit more challenging for me than it might otherwise be. I wasn't able to get enough contrast on the spectrogram with tools I know about to reliably decode the dits by eye, and as mentioned I was struggling to decode it by ear as well given all the background noise. I pulled the audio back into Audition and thought about what options I might have there to process it further. I ran it through the Dynamics processor with an Auto Gate (what I'd call a "noise gate") to see if I could get it to silence the parts of the audio where there wasn't a tone present. I had fairly good success with this after a few minutes of tweaking the threshold, attack, hold, and release settings, and mostly had the audio to the point where it was only the tones coming through without much else. I figured I'd try adjusting the pitch to see if there was another frequency I could understand better. As I was sweeping the pitch up and down, I hit what I assume was the resonant frequency of the bottom of my laptop, and the palmrests started vibrating significantly. I realized this might be an easier route than trying to do it by ear, and tweaked the pitch to get the vibration the strongest I could. While I was altering it, I slowed it down a little bit as well to make it easier to transcribe. I opened a notepad and played the audio back, typing dits and dahs into it as I felt the vibrations, and marking a few sections with question marks where I wasn't 100% sure if there was a dit or not. ########################################## ### Phase 3 - Semi-automated Decoding? ### Now that I had all the dits and dahs in a file, I threw together a bit of Ruby to filter out the notes I'd made (question marks where I wasn't sure, timing markers every minute or so in case I had to go back and check something later, etc.) and convert the input into text. I ended up with a lot of numbers but with a few seemingly random letters mixed in. Somehow at the time it didn't occur to me what this meant, but I'll blame that on it being late in the CTF and my brain starting to shut down :) I figured that maybe this was just the "secret" part of the flag, and I was supposed to enter the boilerplate part (flag{standardtext:) by hand followed by this text. That wasn't accepted, so I figured I must have a typo somewhere in the flag data. I went through the file again and focused extra on the parts that I'd marked with question marks earlier, comparing what I could feel, hear, and see in the spectrogram to try to get it as accurate as possible. I found a couple errors, but still wasn't getting the "flag" accepted using this method. At this point I took a step back and looked over the data I had and what it might represent. I realized that most of the letters were a-f, so the data must have been hex but with some errors that threw me off. I focused on the letters that weren't in that range and found an error in the translation table I was using, which had resulted in it translating what should have been 'd' into 'q' instead. With that fixed, only a couple letters were outside that range, and reviewing the audio found transcription errors in each of those instances. I fixed all of those I could find, updated the decoder script to also turn the hex into text, and finally received the valid flag! ################################## ### Lessons Learned/Reinforced ### - As with many CTF challenges, stepping away for a bit is always a good idea whenever I get stuck, it usually results in new ideas that are often helpful in getting me un-stuck. My dog helps enforce this, I should listen to his advice to go for a walk mid-CTF more often :) - Focusing too much on automation wasted more time than was needed. I probably spent half the total time on this one on the automatic route, so had I just jumped to doing it by hand right away I probably would have had it completed in half the time. It's worth evaluating early on how much time it seems like the automation will save, and just doing it by hand if it's not worth it.