xenia 553522c799 | ||
---|---|---|
.. | ||
COSMOS.png | ||
COSMOS_MM.png | ||
COSMOS_enable_telemetry.png | ||
Dockerfile | ||
README.md | ||
connect.py | ||
dump.rb | ||
ghidra.png | ||
launch-docker.sh |
README.md
Bytes Away!
Category: Satellite Bus Points (final): 223 Solves: 11
We have an encrypted telemetry link from one of our satellites but we seem to have lost the encryption key. Thankfully we can still send unencrypted commands using our Cosmos interface (included). I've also included the last version of kit_to.so that was updated to the satellite. Can you help us restore communication with the satellite so we can see what error "flag" is being transmitted?
Write-up
by haskal
Two files are provided for this challenge, one contains the kit_to.so
and the other contains a
full COSMOS directory tree for accessing the virtual satellite, which can
be booted up with the provided netcat endpoint. COSMOS is an open-source command and control
framework for satellites using NASA's Core Flight System. The provided
COSMOS directory contains everything we need to interact with the virtual satellite, and the
kit_to.so
is part of the code that runs onboard the actual satellite. Booting up COSMOS is
enormously complicated, so Docker can be used to automate the setup. We adapted the Ball Aerospace
COSMOS Docker image, and created a script to configure COSMOS to connect to the CTF's satellite
instance automatically by writing the configuration file at
cosmos/config/tools/cmd_tlm_server/cmd_tlm_server.txt
.
When COSMOS is successfully connected to the CTF instance it looks like this (no themes were
installed in the Docker container so it looks like Windows 95, I'm so sorry,)
COSMOS can be used to send commands with the Command Sender, and we can send for example a command for ENABLE_TELEMETRY, which causes the satellite to start sending telemetry. However these are encrypted, so COSMOS cannot understand them.
We also discover another present subsystem called MM
, which allows for reading and writing
arbitrary memory on the satellite (how useful!) as well as interacting with memory by symbols
(extremely useful!).
The provided kit_to.so
contains the code used by the satellite to transmit telemetry to COSMOS.
We used Ghidra to analyze the binary (which helpfully includes symbols
and debugging information, and that makes our lives way easier for this problem). We discovered that
it uses AES CBC with a key and IV retrieved with external functions get_key
and get_iv
that are
not present in the binary. However, these are stored in known locations in memory, which means it
would be possible to read the AES key and IV using the PEEK_MEM command in COSMOS and then decrypt
the telemetry packets, but there's an easier way. The code contains a function KIT_TO_SendFlagPkt
which (as you might guess) sends the flag via encrypted telemetry, and this also writes the flag as
an intermediate value to a known memory location. PIE is enabled for this binary, however since the
PEEK_MEM command allows looking up memory by symbol name the address randomization is very trivially
bypassed.
Inspecting the structure of KitToFlagPkt
shows that the flag is located at offset 12 and is (up
to) 200 bytes long. We created a Ruby script in the COSMOS Script Runner to execute PEEK_MEM
commands for each byte in the flag range, based on the command COSMOS outputs to the console when
running the command manually in the GUI. Note that in order for the function KIT_TO_SendFlagPkt
to
be called at all, we must first run the ENABLE_TELEMETRY command even though we're not going to look
at any actual telemetry.
12.upto(212) { |off|
offset = off
cmd("MM PEEK_MEM with CCSDS_STREAMID 6280, CCSDS_SEQUENCE 49152, CCSDS_LENGTH 73, "
+ "CCSDS_FUNCCODE 2, CCSDS_CHECKSUM 0, DATA_SIZE 8, MEM_TYPE 1, PAD_16 0, "
+ "ADDR_OFFSET #{offset}, ADDR_SYMBOL_NAME 'KitToFlagPkt'")
}
This directly prints the flag to the console, simply decode the hex to get the flag value.