Almost Perfect Remote Signing

In this challenge, we were given the file aprs_out.wav, which seems to contain a radio transmission. After analyzing it for a while, I focused on its name, which pointed me to Automatic Packet Reporting System, a radio-based system for real time digital communications, used primarily by amateur radio operators to exchange real-time informations. Since APRS is transported over the AX.25 protocol, I could use the atest utility that comes bundled in Dire Wolf. The decoded message is a series of APRS packets in the following form:

DECODED[1] 0:02.175 N0CALL audio level = 63(33/33)     
[0] N0CALL>APN001:!4346.02N\01115.45EgHello flag! Pkt 0002/1080

DECODED[2] 0:02.610 N0CALL audio level = 63(32/33)     
[0] N0CALL>APN001:!4346.02N\01115.48EgHello flag! Pkt 0003/1080
.
.
.
DECODED[1069] 8:07.876 N0CALL audio level = 63(33/33)     
[0] N0CALL>APN001:!4345.23N\01130.45EgHello flag! Pkt 1080/1080

I noticed that the latitude and longitude in the packets were always different, and this gave me the idea to try and plot them. To do so, I first needed to understand how to parse every different couple (lat, lon). After reading the APRS Specification Document, I found out that both latitude and longitude follow the format ddmm.hh, where dd represents degrees, mm represents minutes, and .hh represents seconds. To indicate the direction of latitude, the letter “N” represents North, while “S” represents South. Similarly, for longitude, “E” denotes East, and “W” denotes West. This gave me everything I needed to plot the coordinates, which I did with the following code:

import re
from lat_lon_parser import parse
import geopandas as gpd
import matplotlib.pyplot as plt

dec = ""
lat_lon = []

with open("decoded.txt", "r") as f:
    dec = f.read()
    f.close()

pattern = r"!([0-9]{2})([0-9]{2}\.[0-9]{2})([NS])\\([0-9]{3})([0-9]{2}\.[0-9]{2})([EW])"
matches = re.findall(pattern, dec)
for elem in matches:
    lat_lon.append((parse(elem[0] + " " + elem[1] + " " + elem[2]), parse(elem[3] + " " + elem[4] + " " + elem[5])))    

df = gpd.GeoDataFrame({'geometry': gpd.points_from_xy([x[1] for x in lat_lon], [x[0] for x in lat_lon])})
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
ax = world[world.name == 'Italy'].plot(color='white', edgecolor='black')
df.plot(ax=ax, color='red')
plt.show()

After the execution, I could see the plot of the points over a map of Italy (they were all over Florence, not strange considering the theme of the CTF). This is how they looked after zooming on them: Plotted points This seems like a flag. Indeed, if we remove the horizontal and vertical whitespaces, it is easy to read it: DANTE{FLAG_REPORTING_SYSTEM}

Untrue

Thinking sand goes brrr | MS in Engineering in Computer Science @ Sapienza University of Rome | pwning w/ TRX & mhackeroni


2023-06-05