Reverse Engineering Radio Frequency Signals Solutions Walkthrough: Hack-A-Sat Capture the Flag 2023 (Part 2 of 3)

Kyle McClintick
5 min readApr 7, 2024

--

In the last post, I covered RF RE tools, trends in RF RE CTF challenge formats, a process for solving those challenges, and my solution for the starting HACK-A-SAT 4 RF RE challenge, “QAM”.

In this post, I will cover my solution for the “Dashing” challenge, which will showcase examples of previously undiscussed topics from the solution framework: channel characterization, embedded python GRC blocks, and filtering.

The problem statement for “Dashing” was to investigate the odd file “beepboop.wav” and discover a hidden message. That’s it. Given the number of ways a time-series can modulate a digital message, it’s a very daunting task. Before we panic, let’s utilize the solution framework we defined last time and characterize the channel.

The signal seems to be comprised of a series of “dashes” and “dots”, and the challenge name is “dashing”…this seems to be a Morse code signal. Since all flags in this competition have been hex-to-ASCII, it’s reasonable to assume we will only see letters A-F and numbers 0–9, so we can ignore a lot of the Morse alphabet.

Doing this manually was tedious, but we were able to do it in a few minutes plus a few minutes to check out work. Eventually, we get the flag.

While this works just fine for a shorter flag, this solution does not scale. Imagine if we made a mistake for a 30-minute signal, it would take forever to find out mistake.

Let’s look at a number of ways to design a Morse code demodulator. One way is using GNU Radio Companion (GRC), which is one of the stream-based processing tools we highlighted in the last post.

However, Morse code has been around for a long time! Perhaps there is a tool someone else has made for this.

A quick google search reveals this tool, however even here there is no free lunch. To get something that looks like a flag, we still need to filter the noise away from the out-of-band frequencies, and move the center frequency to the carrier the website expects. Our team’s MATLAB code, which is one of the array-based processing tools covered in the last post, is provided below.

filename = 'C:\Users\KY29226\Documents\beepboop.louder.wav'
[y,Fs] = audioread(filename);
nfft = 2048;
% Inspect file to assess modulation
figure, spectrogram(y(1:1e6,1), ones(nfft,1), nfft*3/4, nfft, 'centered', Fs, 'yaxis', 'MinThreshold',-85); colormap gray; drawnow
% Filter the baseband signal
% This first filter is a coarse filter to remove noise before frequency translation so as not to fold-in noise unnecessarily
b = fir1(2048,[0.004 0.01]);
y11 = filter(b,1,y);
% Assess filter results
figure, spectrogram(y11(1:1e6,1), ones(nfft,1), nfft*3/4, nfft, 'centered', Fs, 'yaxis', 'MinThreshold',-95); colormap gray; drawnow
% Translate audio signal from 200 Hz to 700 Hz (because that is/was the mil std and the bot needs it)
y2 = y11.*cos(2*pi*.49e3/Fs.*([1:length(y)]'));
% Assess the translation and note the extraneous image signals
figure, spectrogram(y2(1:1e6,1), ones(nfft,1), nfft*3/4, nfft, 'centered', Fs, 'yaxis', 'MinThreshold',-85); colormap gray; drawnow
% Filter the image signal
b = fir1(2048,[0.03 0.04]);
y3 = filter(b,1,y2);
% Assess your filter
figure, spectrogram(y3(1:1e6,1), ones(nfft,1), nfft*3/4, nfft, 'centered', Fs, 'yaxis', 'MinThreshold',-105); colormap gray; drawnow
% Amplify the weak audio
y4 = y3*50;
% Save the new audio file
filename_out = 'C:\Users\KY29226\Documents\processed.beepboop.louder.wav'
audiowrite(filename_out,y4,Fs)
% Upload the audio file to https://morsecode.world/international/decoder/audio-decoder-adaptive.html
% Run the online decoder, copy and paste the results
str = '6 6 6 C 6 1 6 7 7 B 5 4 7 5 7 2 6 B 6 5 7 9 5 4 7 2 6 F 7 4 7 3 5 4 6 F 5 7 6 1 7 4 6 5 7 2 4 7 4 7 5 7 6 8 6 5 7 2 6 5 4 9 7 3 5 2 5 0 5 4 5 7 6 8 6 5 7 2 6 5 4 9 7 3 5 4 4 6 3 3 3 4 5 2 5 2 5 4 6 8 6 5 5 7 6 F 7 2 6 C 6 4 5
7 6 F 6 E 6 4 6 5 7 2 7 3 7 D';
% Manipulate the string into two nibbles
str = [str ' '];
str2 = reshape(str, 4, [])
str2(4,:) = []
str2(2,:) = []
str3 = str2';
% Convert hex to string
char(hex2dec(str3))'
% ans = 'flag{TurkeyTrotsToWaterGGWhereIsRPTWhereIsTF34RRTheWorldWonders}'

In this post, I covered our teams solution for the “Dashing” challenge. I covered channel characterization, GRC and GRC out-of-tree modules, and how to use online tools with waveforms processed in array-processing tools.

In the next post, I will cover the last two parts of the solution pipeline: resampling and synchronization, as exemplified by my “Fauxy Lady” challenge solution.

--

--

Kyle McClintick

PhD in electrical engineering with a focus on AI and security