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.