Written by: Milenko Starcik, Andrzej Olchawa, Ricardo Fradique, Ayman Boulaich
NASA’s fprime (F’) is an open-source software framework for developing and deploying embedded systems, particularly for spaceflight missions. Maintained and developed by NASA’s Jet Propulsion Laboratory (JPL), it allows for modular system development by defining the exact interactions of components using a standard interface. But our latest security scan of fprime v3.4.3 has uncovered a variety of critical vulnerabilities, including Remote Code Execution (RCE), Denial of Service (DoS), and Cross-Site Scripting (XSS), that can be exploited to attack mission-critical systems.
Key Findings
We have uncovered the following vulnerabilities in the NASA fprime and fprime-gds software:
- Denial of Service (DoS) via Queue Overflow – CVE-2024-55030
- Multiple XSS vulnerabilities in fprime-gds – CVE-2024-55029
- Template Injection on the Dashboard Tab of fprime-gds – CVE-2024-55028
These vulnerabilities are high-risk, mainly when fprime is used for space mission operations and telemetry processing.
Watch the XSS injection in action
- fprime
fprime is a spaceflight application and embedded system rapid development and deployment software framework. The core of fprime is the F′ software architecture, which allows projects to be separated into isolated components bound together by communication ports. A component is a specific portion of the system, and components provide input and output ports for contact with other elements so that the system is modular and organized.

Figure 1: Component diagram.
These ports define the types of messages or information that a component can send or receive and represent a standard interface for system components to exchange information. The components communicate regarding events—notices of particular system events—and commands, which are explicit requests for specific activities. Based on events, this model gives flexible, real-time access to the system.
Projects are created automatically by the autocoder, a Python software program. The autocoder inputs XML representations of components, port types, data types, and topologies and generates C++ code (ac.cpp/hpp files). When compiled, the generated code communicates with the graphical user interface (GUI) via a middleware layer, or TTS. GUI and TTS are part of the fprime-gds system, which runs in Python, facilitating easier integration and system control.
Figure 2 depicts the fprime architecture, which indicates how these components work together and communicates, maintaining the flexibility and scalability required for high-complexity space missions.

Figure 2: Architecture diagram.
1.1 Denial of Service through Queue Overflow
The test showed that the Command Dispatcher Service is not processing several commands well. This service will relay incoming commands from an external source, like a sequencer or ground system, to the destination components.
The issue was demonstrated by repeatedly modifying the send_command function in pipeline/standard.py to send commands in a loop (Figure 3).

Figure 3: Sending commands in an infinite loop.
Attached to the TTS middleware, the FSW, running in gdb for debug reasons, receives a stream of commands and dies with a sigabort signal.

Figure 4: sigabort in FSW.

Figure 5: Debug the backtrace of the crash.
Backtrace analysis (Figure 4 and Figure 5) indicates that the prior function to invoke the assertion check was CommandDispatcherComponentBase::seqCmdBuff_handlerBase(), which processes received command buffers (Figure 6).

Figure 6: seqCmdBuff_handlerBase function.
Because many commands were sent, debug messages were used instead of breakpoints to find the issue (Figure 7).

Figure 7: Debug prints.
The fault was diagnosed from the variable qStatus, which handles a non-blocking queue processing serialized commands. Error code 8 means that the queue is full (Figure 8).

Figure 8: Assertion printout.
Looking at QueueCommon.cpp (Figure 9), we found the send() method called bareSendNonBlock(), which attempts to place data onto the queue with queue.push().

Figure 9: QueueCommon send function.
The failure comes when queue.push() returns the QUEUE_FULL status, i.e., the queue is full (Figure 10, Figure 11, Figure 12).

Figure 10: Queue send function.

Figure 11: Queue bareSendNonBlock function.

Figure 12: BufferQueue push function.
The specific return that leads to the failure comes on line 55 (Figure 13).

Figure 13: Return printout.
The isFull() function verifies if the queue has attained depth by checking if m_count equals m_depth (Figure 14).

Figure 14: BufferQueue isFull function.
During run time, it was verified that m_count does indeed reach m_depth, which leads to an assertion failure (Figure 15).

Figure 15: m_count and m_depth printout.
While assertions are not vulnerabilities, the issue is how they are handled. The system continuously tests assertions, and upon the failure of an assertion, it sends a sigabort signal and kills the system. This kind of behavior is related to CWE-617 (Insecure Assertion Handling).
The weakness is also a misconfiguration because the fpp interface of the Command Dispatcher does provide support for queue overflow handling to block, hook, or drop commands instead of assert. Yet, the default is assert (Figure 16).

Figure 16: CmdDispacher fpp file.
The second weakness is FpConfig.cpp, where FW_ASSERT_LEVEL can be configured to FW_NO_ASSERT to avoid crashes, but the default setups still cause system failure (Figure 17).

Figure 17: FpConfig.cpp.
This is compliant with CWE-453 (Bad Default Configuration).
Besides crashes, all failures also create zombie threads (Figure 18).

Figure 18: Zombie threads.
Valgrind tests revealed massive memory leaks since most memory is not released correctly (Figure 19).

Figure 19: Valgrind output.
Recommendation: First, we recommend changing the default behavior of queue overflow to one of the other safe behaviors they provide (block or drop). Second, we think the sigabort termination is unsafe in itself. We recommend a mechanism that would gracefully handle this event, properly closing sockets and releasing the memory.
1.2 Man-in-the-Middle on fprime
One critical concern identified is that FSW and GDS communication is not encrypted. Without encryption, it is exposed to a Man-in-the-Middle (MitM) attack, in which the attacker can intercept and manipulate packets before reaching the FSW.
With Wireshark’s assistance in capturing the traffic packets between the middleware and the FSW, it was possible to observe that the data in the command packet could be accessed very easily (Figure 20).

Figure 20: Wireshark sniffed packets.
To demonstrate how an attacker could exploit this vulnerability, we set up an environment with two independent virtual machines running FSW and GDS (having different MAC addresses) and a third virtual machine acting as the attacker. The attacker performs ARP poisoning, followed by the MitM attack, routing the traffic through the attacker’s machine (Figure 21).

Figure 21: MitM diagram.
During the research, several attack scenarios were developed to show the potential impact on the fprime framework. A custom tool, dump, was created to facilitate testing these scenarios. The dump tool allows the simulation of malicious actions against communication between FSW and GDS. Dump operates just like the other MitM tools but with a twist. Instead of poisoning the ARP tables of just the victim and gateway, it poisons the ARP tables of the victim hosts so that the traffic will be relayed through the attacker’s computer.
1.2.1 Manipulating Command Packets
The structure of a regular command packet is shown in Figure 22.

Figure 22: Command Packet structure.
- 2 bytes (0xdeadbeef) are used as the packet starting mark before the packet.
- 4 bytes next represent the packet length (relative to the descriptor).
- There is a further 4-byte descriptor which represents the packet type (in our example, 0x00000000).
- The opcode is calculated by adding the base ID for the component and the command number.
- Finally, the packet contains the command arguments and a CRC32 checksum.
By changing the opcode, the entire command can be changed. Figure 23 illustrates how an attacker could intercept a NOP command (opcode 0x500), replace the opcode with 0x602 (command sequence cancel), and send it to the FSW for execution. A simple change like this can significantly modify the command’s intended action.

Figure 23: Command Packet MitM.
1.2.2 File-Uplink Exploitation
It takes sending a sequence of three types of packets to FSW to send a file:
1. Start packet
2. Data packet
3. End/Cancel packet
The command structure for sending files to FSW is shown in Figure 24.

Figure 24: File Packet structure.
- It starts with 2 bytes (0xdeadbeef) to indicate the beginning of the packet.
- This is followed by 4 bytes defining the packet length (from the descriptor).
- Next, the descriptor has 4 bytes, which indicate the type of packet (in this case, 0x00000001).
Following the descriptor:
- Packet type (1 byte): This can be one of the following: START (0), DATA (1), END (2), or CANCEL (3).
- Sequence index (4 bytes): An unsigned integer identifying each packet. The sequence index starts at zero for each file and increments by one for each successive packet in the file.
- Packet data (variable): The data format varies with the packet type.
This structure presents several attack vectors, including:
- Changing the file path,
- Changing the file contents,
which may result in serious malicious activity in the system.
1.2.2.1 Manipulating Start Packets

Figure 25: Start Packet Data Field.
A Start packet contains a packet type of START and a sequence index of zero. The data structure of this packet (illustrated in Figure 25) includes the following:
- File size in bytes (4 bytes)
- Source path length in bytes (1 byte)
- Source path (variable length)
- Destination path length in bytes (1 byte)
- Destination path (variable length)
By modifying the destination path in the intercepted START packet, an attacker can cause the file to be stored in another location, as shown in Figures 26 and 27. This allows attackers to overwrite any file on the spacecraft system FSW can write into, which poses significant security threats.

Figure 26: GDS file uplink request.

Figure 27: File name received on FSW.
1.2.2.2 Manipulating Data Packets

Figure 28: Data Packet Data Field
A Data packet is of type DATA. Its structure (as shown by Figure 26) consists of the following fields:
- Byte offset in the entire file for this packet of file data (4 bytes)
- Length in bytes of file data in this packet (2 bytes)
- File data (variable length)
By modifying the file data field in the DATA packet, we can alter the content of the file being transferred. However, this modification also requires changes to the END packet, which contains a 32-bit checksum value computed from the file data (following the CFDP format). This checksum will be validated by FSW (for file uplink scenarios) or GDS (for file downlink scenarios), as seen in Figure 29 and Figure 30.

Figure 29: GDS file uplink request.

Figure 30: File content received on FSW.
These steps demonstrate that the fileUplink service can be exploited with a Man-in-the-Middle (MitM) attack. The same methodology can be applied in the fileDownlink case, demonstrating how versatile the attack technique is.
1.2.3 File-Downlink Exploitation
Here, we will chain two vulnerabilities to steal sensitive data from FSW.
The chain of exploitation starts with manipulating a fileDownlink command (namely, SendPartial) that is employed to download a file from FSW so that another file is downloaded by replacing the source path of the command rather than the specified file. The manipulation will trigger the downlink process, during which data is transferred from the spacecraft to the ground.
An attacker can sniff the telemetry packets that the spacecraft is transmitting and then retrieve the DATA field of such packets and rebuild the file from this DATA field. Let us understand this as an example. Suppose users are attempting to download the file /fprime/files/transfer/downlink/test from the spacecraft, as shown in Figure 31.

Figure 31: fileDownlink command.
Let’s also assume there is a /secret file containing sensitive information. By altering the downlink command, the spacecraft will send the content of the secret file instead of the originally requested file, as shown in Figure 32.

Figure 32: Sniffing DATA Packets.
As Figure 33 shows, the attackers can sniff downlink packets, reassemble them, and capture the secret file, thus exfiltrating sensitive data from the spacecraft.

Figure 33: secret file exfiltrated.
1.2.4 Remote Command Execution (RCE)
Another service that may lead to unexpected behavior in a default installation is the fileManager service, which allows commands to be run on the onboard software via the ShellCommand command.
Using the same method as a Man-in-the-Middle (MitM) attack, it is possible to manipulate any command packet and send a ShellCommand command with a malicious payload to successfully achieve Remote Command Execution (RCE) on the FSW. In our scenario, we are sending the fileDownlink.SendFile command, which is intercepted and modified into a malicious fileManager.ShellCommand, as shown in Figure 34 and Figure 35.

Figure 34: Environment set up.

Figure 35: RCE on FSW.
As seen in this example, we use this technique to establish a reverse shell on the system where the compromised FSW instance is running. Though a reverse shell payload is used in this example, this is purely for demonstration. In a real-world scenario, this would be unfeasible for a flight mission. However, this technique shows how attackers can execute any command on the spacecraft running FSW.
This illustrates how an RCE can be escalated from a MitM attack on the vulnerable FSW instance. RCE is a critical attack vector to watch out for because it is one of the most dangerous types of vulnerability, giving an attacker complete control of the system. With RCE, attackers can perform malicious actions such as:
- Executing arbitrary commands or programs
- Exfiltrating sensitive information
- Modifying system files
- Installing malware
- Escalating privileges to gain elevated access rights
Recommendations: Every command carries a checksum that guarantees its payload is correct and what was sent from the ground before it is run on the spacecraft. But as we demonstrated, it is very easy to abuse this protection without encryption through tampering with the checksum. All attack scenarios could be alleviated by inserting authenticated and encrypted communication between the pieces running on the ground and between space and the ground.
Additionally, File-Uplink and File-Downlink services use the CFDP checksum calculated only over the data portion of the file transfer. This is being performed as a part of a functional requirement and separately from security needs. Although it will not eliminate the vulnerability, we recommend that the checksum be calculated over the whole file transfer (i.e., start, data, and end packets) or insert an additional CRC for that purpose.
2. FPRIME-GDS
The fprime-gds is a collection of classes and utilities that provide an interface for fprime deployments to show telemetry and events and to issue commands. Even though it doesn’t require a UI, the default installation spawns an instantaneous web UI for ease. The UI utilizes Vue.js for the front end and Python/Flask for the back end and network interaction.
2.1 FPRIME-GDS Endpoints
Upon loading, the web UI sends a request to the following endpoints at a fixed refresh rate:
- /channels?session=<sessionID>
- /logdata?session=<sessionID>
- /events?session=<sessionID>
- /commands?session=<sessionID>
- /upload/files?session=<sessionID>
- /stats?session=<sessionID>
This returns a JSON response for each endpoint with all the information required to update the UI on what’s happening on a spacecraft. In addition to this, it is also possible to obtain the dictionary of all events, channels, and commands available for the current application by querying:
- /dictionary/<events/channels/commands>
Files can be uploaded by setting a destination path with:
- /upload/destination
Then, POSTing the file contents to:
- /upload/files
There’s also a page that helps users create and upload files containing sequences of commands to be run at specified intervals, available in:
- /sequence
This uses the file upload endpoint to upload the sequence file, which can be started by sending a command to the spacecraft. PUT requests can send commands to:
- /commands/<commandID>
To trigger a remote command, it is further required that the request sends a JSON payload containing the key “0xfeedcafe”. This is done to prevent any accidental command issues. All command arguments are also sent in the same JSON, in the form (from /fprime_gds/flask/commands.py):
Data: {
“key”: “0xfeedcafe”, # A key preventing accidental issuing of a command
“args”: {
<arg-key>:<arg-value>,
…
}}
2.2 Cross-Site Scripting (XSS) on the Run Commands Table
In this review, we found that the default fprime-gds GUI is not correctly protecting the arguments and event strings passed/returned before they are included in the webpage. As they are delivered through the web API and inserted into the view at a set refresh rate, this allows for the injection and execution of malicious scripts into any browser that had loaded the page when the injection took place without user interaction.

Figure 36 – XSS payload being executed without user intervention in the Commands Tab.
This can be illustrated by posting a command with an XSS payload in the arguments, as depicted in Figure 36, using the cmdDisp.CMD_NO_OP_STRING command. The argument string is inserted directly into both the Commands and Events tables, run in each place simultaneously, and illustrated here by rendering an alert popup when run.
While the default configuration restricts the payload’s length to the maximum accepted string size of 40 characters, it is reasonable to assume that custom commands created for regular use might increase this value for operational use, increasing the potential risk of this vulnerability.
To demonstrate this, we’ve created a custom HelloWorld command that takes a string of up to 300 characters as input and outputs it in the event description:

Figure 37 – Custom component definition will reflect an input string of up to 300 characters as an event

Figure 38 – A custom command that reflects user input on the event description.
This allowed us to inject a more extended script. Since there is little filtering in effect to remove characters used in the command structure itself, the script was base 64 encoded before being submitted and decoded before being run:

Figure 39 – Script injected in the payload. This sends a cmdDisp.CMD_NO_OP_STRING command with “Sent command from script” argument.

Figure 40 – Final injected script, with b64 encoded string.

Figure 41 – Command being sent by the injected script with no user intervention upon receiving the helloWorld command telemetry.
This allows the attackers to inject any command into the spacecraft using the XSS vulnerability pointed out above without the user’s knowledge.
XSS on the File Uplink Page
We have also identified another XSS-vulnerable point on the uplink file page. The /upload/destination file destination parameter is reflected insecurely in FileOpenError in the Events tab. An attacker can inject a malicious script into the file name parameter and execute it automatically without user involvement. Once more, the impact is limited as this parameter is limited to 40 characters in the default configuration. This can potentially be extended to different applications.
This can be observed in Figure 42. The payload is injected into the Events tab and executed by issuing a request to /upload/destination with the XSS payload as the destination path and then uploading a non-existent file to /upload/files.

Figure 42 – XSS payload is executed in the Events tab without user intervention.
2.2.4 Template Injection and XSS on the Dashboard Tab
Finally, a template injection vulnerability was found in the Dashboard tab. The tab is used so that users can create, save, and load their custom dashboard UI. This input is, however, directly inserted into a Vue template with no additional parsing:

Figure 43 – The method in dashboard.js directly includes user input into a Vue template.
Constructing a layout file that includes and executes a malicious script at load time is feasible. Figure 44 shows a layout file with a JavaScript payload that will send a cmdDisp.CMD_NO_OP_STRING command, with the string “Sent command from layout”, on load, with no further user interaction (beyond file loading).

Figure 44 – Layout file including a malicious javascript payload to send a command.

Figure 45 – Custom dashboard created from the malicious layout file, with the command’s menu displaying the last sent command.
In this case, the layout file is restricted to just under 1Mb in size, which is ample space to reproduce any interaction the user makes with the page and also to modify the functionality of the entire UI to mask malicious functionality within the background (such as preventing events from taking place or hiding commands sent by the UI). Figure 46 shows this example: when this layout file is loaded, a new command is dispatched and then deleted from the UI after 1s, hiding the actions from the user:

Figure 46 – Modified payload to remove the injected command from the UI.

Figure 47 – Custom layout with the injected script. The events block shows that a command was dispatched and completed that was not displayed in the Command table.
2.5 Recommendations
All fprime-gds issues can be mitigated by safely sanitizing all user inputs before their use in the UI.
For outputs 2.2 and 2.3, unsafe input is fed to the web UI once it has already been processed through the GDS back end in Python. Python includes the HTML module that may be used to sanitize all inputs to the UI (sanitized_str = html.escape(input_str)), potentially blocking this issue for all parameters.
Entirely preventing finding 2.4 may be slightly more difficult without application structure modification. The application creates a new template dynamically based on user input, so escaping all of it would keep the functionality from occurring. However, it should be possible to include some filtering to remove some special characters that would not appear in a standard template to avoid script injection to some extent. It is also a good practice to let users know what a harmless layout file looks like and how to manually inspect and detect possible exploits in typical layout files.
3. Final Recommendations
Given the gravity of these vulnerabilities, we strongly recommend the following countermeasures:
- Secure fprime Communication: Encrypted communication between FSW and GDS over all communication channels.
- Improve Input Handling: Sanitize all inputs in every component, particularly in prime-GDS.
- Strengthen File Transfer Security: Have integrity checks and authenticate file operations.
- Enhance Command Validation: Sign command requests via cryptographic signatures to authenticate them.
- Review Default Configurations: Set default assertion so as not to crash systems unnecessarily.
These results underscore the importance of robust cybersecurity within spacecraft software systems. With increasing reliance on open-source frameworks for space missions, it is imperative to secure these tools to prevent their misuse by competitors.
Contact us for more information and check our other vulnerability disclosures.


