Taking a look at the example signal
The first thing I did was to open this signal file with Universal Radio Hacker to see what it looks like.
After setting the sample rate in the signal details window (the i
button), I could measure the time between two moments of the signal by selecting it and looking at the bottom of the window.
With that, we can see that the duration of the signals is coherent with the transmission parameters we were given. This also becomes obvious this signal is the string HELLO
, since we can see that the signal represents .... . .-.. .-.. ---
, which is HELLO
in morse. The signal we need to build is clear now.
Building our own signal
IQ files are just a format for storing the I and Q composantes of a signal. We can read it in Python using numpy and convert it to a format we can work with, where each sample of the signal is represented by its I and Q coordinates in the complex plane.
This becomes an array on complex numbers with one element of the array being one sample.
Now, all we have to do to create our signal is to go through each morse character and add the right number of samples at 1 + 1j, representing the HIGH level in the signal. We must also not forget to add the right number of samples for the spacing between everything.
Since we know all the parameters, we can script it to create our signal from scratch:
from pwn import *
import numpy as np
import base64
samp_rate = 24000
dot_duration = 1 / 1000
dash_duration = 5 / 1000
pause_duration = 1 / 1000
letter_separation_duration = 5 / 1000
word_separation_duration = 20 / 1000
high = 1 + 1j
low = 0j
c = remote("challenges.france-cybersecurity-challenge.fr", 2251)
can_i_get_the_flag = [
["-.-.", ".-", "-."], # CAN
[".."], # I
["--.", ".", "-"], # GET
["-", "....", "."], # THE
["..-.", ".-..", ".-", "--."] # FLAG
]
flag_signal = np.array([])
for i, word in enumerate(can_i_get_the_flag):
for j, letter in enumerate(word):
for k, char in enumerate(letter):
if char == ".":
flag_signal = np.append(flag_signal, [high] * int(dot_duration * samp_rate))
elif char == "-":
flag_signal = np.append(flag_signal, [high] * int(dash_duration * samp_rate))
if k < len(letter) - 1:
flag_signal = np.append(flag_signal, [low] * int(pause_duration * samp_rate))
if j < len(word) - 1:
flag_signal = np.append(flag_signal, [low] * int(letter_separation_duration * samp_rate))
if i < len(can_i_get_the_flag) - 1:
flag_signal = np.append(flag_signal, [low] * int(word_separation_duration * samp_rate))
flag_signal = flag_signal.astype(np.complex64)
flag_signal.tofile("flag_signal.iq") # for later verification
encoded_signal = base64.b64encode(flag_signal.tobytes())
c.recvuntil(b"> ")
c.sendline(encoded_signal)
print(c.recvline().decode("utf-8"))
We execute the script, and we get the flag:
Before we submit the flag and get our points, let’s take a look at our flag signal real quick:
We can recognize the same morse message we stored in the can_i_get_the_flag
variable, and the duration of the characters and the spacing seems correct.
Flag : FCSC{e8b4cad7d00ca921eb12824935eea3b919e5748264fe1c057eef4de6825ad06c}