Written by: Andy Olchawa, Milenko Starcik
The NASA – National Aeronautics and Space Administration AMMOS Instrument Toolkit (AIT) is a versatile, Python-based software suite designed to handle a wide range of tasks associated with Ground Data Systems (GDS), Electronic Ground Support Equipment (EGSE), commanding, telemetry uplink/downlink, and sequencing for both instrument and CubeSat missions. Building on the legacy of tools developed for numerous International Space Station (ISS) missions, the NASA Jet Propulsion Laboratory AMMOS Catalogue offers a set of generalised and expanded solutions to meet the complex demands of modern space exploration. Whether managing data from cutting-edge instruments or ensuring seamless communication with CubeSats, this toolkit provides the reliability and flexibility you need to achieve mission success.
We have discovered several severe vulnerabilities at the core of the AIT-Core version 2.5.2:
- SQL injection – CVE-2024-35056
- Multiple Local Code Executions (using eval) – CVE-2024-35057, CVE-2024-35058
- Multiple Local Code Executions (using Pickle) – CVE-2024-35059
- Multiple Local Code Executions (using YAML) – CVE-2024-35060
- Remote Code Execution (including via MitM) – CVE-2024-35061
Watch the RCE via MitM demo in action:
SQL Injection
AIT-Core has a functionality that provides an interface to a back-end database containing the AIT Telemetry, AIT Commands, and AIT Event Records. By default, AIT-Core provides the implementation specific to the InfluxDB and SQLite3. Both back-end implementations can be found in the ait/core/db.py Python file, and both implement a function that allows the retrieval of Telemetry packets called query_packets. In both cases, this function builds a SQL query string in an insecure way, which enables the injection of SQL statements, see Figure 1 and Figure 2.
Although this SQLi is harmless in the case of InfluxDB due to the limited SQL functionality in this database engine, the same insecure implementation approach has been adopted for SQLite, which can harm the system if abused by a bad actor. Although the packet ID is verified later down the execution flow, the verification is limited and only checks whether a packet exists. This doesn’t prevent the SQL Injection. However, it somewhat increases the exploitation difficulty, as to pass this check, the bad actor must modify the tlm.yaml file containing the packet definitions.
Figure 3 shows an example of a packet definition that allows the load of an SQLite database extension using the SQL Injection point described above. This can be tested by requesting the back-end query_packets function to execute the malicious statement (see Figure 4).
Depending on the system configuration within which the AIT-Core framework is used, this vulnerability, although relatively challenging to exploit, can lead to command injection, lateral movements, and even privilege escalation. Additionally, given that the AIT-Core is a framework that can be exposed to processing a significant amount of data (i.e., Telemetry packets), it is very likely that the back-end functionality will be extended to support other databases (e.g., MySQL) by following the exact implementation approach as for InfluxDB and SQLite, this way increasing a possible attack surface.
Recommendations
Back-end database implementation also provides the insert function that handles the build of SQL query statements in a partially secure way by using the values (‘?’) separately and deferring the query build to the database library; see Figure 5. The exact implementation approach is recommended for functions that build a SQL query string.
Multiple Local Code Executions (using eval)
AIT-Core is a highly customisable framework that allows its users to employ Python expressions in some regions of its functionality. However, in some cases, the framework uses the eval function, which is inherently insecure, to evaluate the Python code. The most impactful occurrences of those calls have been found in the API wait function ait/core/api.py (see Figure 6) and ait/core/tlm.py (see Figure 7) files.
API
AIT-Core provides an API wait function that waits either for a specified number of seconds or for a given condition to be True. The conditions may take several forms: Python string expression, lambda, or function. Regardless of the chosen form, they are passed as a string to the wait function, which evaluates that string using eval. Although this functionality is handy, it is prone to the Local Code Execution vulnerability, which is also very easy to exploit.
Figure 8 shows an example of a malicious Python expression, while Figure 9 demonstrates the execution of that payload to achieve the Local Code Execution.
TLM
Similarly to the API, TLM allows for the evaluation of Packet Expressions, which can also be Python code. The actual expression is provided in the Packet definition and evaluated every time it is called. To exploit this vulnerability, it is first necessary to modify the packet definition by including the malicious expression, which will be executed every time AIT-Core references that updated attribute.
Figure 10 shows an example of a malicious packet expression, which, in this case, imports the os Python module and executes a system command. Figure 11 presents an example of how and when this expression will be evaluated. First, the packet definition is loaded into the TLM module (line 3). The packet is created (lines 4 and 5), and the attribute containing the malicious expression is called within the print function, thereby evaluating this malicious expression (line 7).
Figure 12 demonstrates the execution of the above test case, showing how the earlier expression allows for achieving the Local Code Execution.
Recommendations
Although the Python eval function is handy, it is inherently insecure. We recommend using third-party libraries that provide the same functionality but sanitise the input to handle expression evaluation securely.
Multiple Local Code Executions (using Pickle)
AIT-Core extensively uses Pickle. As per Python documentation, this library is insecure and allows bad actors to achieve Command Execution. One example is loading a leap seconds configuration file, part of the DMC module. According to AIT-Core documentation, the ait.dmc module provides utilities to represent, translate, and manipulate time, building upon Python’s datetime and timedelta data types. When the leapseconds.dat file is loaded, AIT-Core uses Pickle to process the file (see Figure 13).
If the bad actors can access the AIT-Core configuration file, they can change it to point to their malicious pickle file (see Figure 14), which will then be executed by the AIT-Core DMC module (see Figure 15).
Other areas where the pickle is used and can lead to Local Command Execution are the following modules:
- Table
- Util
- Data Archive
- Limit Monitor
- Open MCT Plugin
Recommendations
The pickle library is insecure and can lead to Command Execution vulnerabilities. Therefore, it should not be used, or the content of the loaded pickle files should be sanitised. This is just one example of how AIT-Core utilises the Pickle library, which we decided to use to demonstrate how it can be exploited.
Multiple Local Code Executions (using YAML)
AIT-Core uses the YAML Python library (pyyaml) in version 5.3.1, which is vulnerable to Command Execution vulnerabilities. By crafting a malicious YAML file, the bad actors can achieve a Code Execution on the target system that runs AIT-Core. Figure 16 shows an example of a malicious YAML file containing a command that will be executed upon loading that file.
To demonstrate this, we have created a simple Python script that imports the TLM module and attempts to read Telemetry definitions from a YAML file (see Figure 17).
Once the script is executed, the AIT-Core TLM module loads the file, and due to YAML vulnerability, the command included in the malicious file is executed on the target system (see Figure 18).
As in the previous case, AIT-Core extensively uses the YAML library. If a bad actor can access any of the YAML files used by AIT-Core or the main configuration, they can exploit the Command Execution vulnerability. Figure 19 shows a snippet of the main AIT-Core configuration file referring to other YAML files and the framework areas this vulnerability affects.
Once the script is executed, the AIT-Core TLM module loads the file, and due to YAML vulnerability, the command included in the malicious file is executed on the target system (see Figure 18).
As in the previous case, AIT-Core extensively uses the YAML library. If a bad actor can access any of the YAML files used by AIT-Core or the main configuration, they can exploit the Command Execution vulnerability. Figure 19 shows a snippet of the main AIT-Core configuration file referring to other YAML files and the framework areas this vulnerability affects.
Recommendations
PyYAML offers a safe_load method that uses the SafeLoader class, which is recommended in version 6.0 and later. This method only uses standard YAML tags and avoids constructing arbitrary Python objects. Using SafeLoader ensures you’re only reading data objects composed of Python primitives and basic data structures, which is usually sufficient. If you require state and behaviour, PyYAML can derive from the YAMLObject class to create safe, user-defined types recognisable by SafeLoader.
Remote Code Execution
AIT-Core comes with a server that enables the ZMQ topic/streams to make telemetry and telecommands packets available remotely, i.e., via API script. To start a simple server, importing an Instrument from the AIT-Core API and instantiating it is enough (see Figure 20).
The created instrument is now ready to subscribe to a telemetry source. For this purpose, we used the TLM module to create a Telemetry simulator, which retrieves a definition of a simple TM packet and sends it every 5 seconds (see Figure 21). In addition, to make the test more realistic, we have set up the TLM Instrument and TLM simulator on two different hosts. To exchange the telemetry, the AIT-Core opens a new port (5560 in our case), establishes the connection between the processes, and communicates using ZeroMQ messaging.
By monitoring the network traffic, we captured the TCP Frames exchanged between the instrument and the TLM simulator (see Figure 22). Given that the ZeroMQ communication used in the AIT-Core is unencrypted, we were able to capture the raw TCP Frames and analyse their content. The Python Pickle object serialisation/deserialisation library powers the TLM functionality. As per Python documentation, this library is insecure and allows bad actors to achieve Command Execution. Given that the communication is unencrypted, a bad actor can conduct a Man-in-the-Middle (MitM) attack to capture the raw TCP Frames and modify their content. With Pickle in the loop, this can lead to an RCE on either of the hosts in this communication link without access to any of them.
We have used our tool to conduct a MitM attack to demonstrate this. First, based on the previously captured TCP Frames, we reconstructed a valid TM packet, which, when encoded, looks like the data shown in Figure 23.
Knowing that once the TLM Instrument receives this data, it will try to load it into the Pickle library, we have developed the following exploitation scenario:
- Craft a payload that opens a reverse shell.
- Capture a TCP Frame that contains a valid ZMQ topic and TM packet data.
- Follow the same procedure as the TLM module to craft a new TCP Frame.
- Pickle the reverse shell payload twice (in the original procedure, AIT-Core includes the TM Packet UID, but for exploitation, it is unnecessary).
- Include the ZMQ topic.
- Replace the data field of the captured TCP Frame with the maliciously crafted one.
Figure 24 shows an example of a reverse shell payload that we have decided to use for this exploitation scenario.
After performing all required steps (encoding, dumping data with pickle, and adding the topic), the new TCP Frame data field looks like Figure 25.
Using our tool to conduct the MitM attacks at the TCP Frame level, we could replace a valid TCP Frame with one containing the reverse shell. Figure 26 shows the execution of our exploit. Figure 27 shows how to capture a valid exchange between the TLM instrument and the TLM simulator. The TLM Instrument receives a malicious payload (see Figure 28), which is then executed and opens a reverse shell to the attacker’s host (see Figure 29).
Recommendations
In the exploitation scenario described above, we see two following vulnerabilities:
- ZMQ communication is unencrypted.
- Use of Pickle
To prevent the RCE, we recommend resolving both issues. Although replacing the plain ZMQ communication with ZMQ SSH Tunnelling might be tempting, more is needed. It will mitigate the MitM attacks; however, given that the TLM instrument opens a port and connects to a telemetry source without any verification, another attack vector emerges for exploitation – in case the bad actor can access the telemetry source host, they can stop a telemetry source and start their own with a malicious payload.
Further readings
Suppose you’d like to learn more about spacecraft mission control software security. In that case, you can just read our articles on vulnerabilities in Open MCT (first and second) and YAMCS (first and second).
To highlight the importance of the software supply chain, we have further published exploits for the OSGi console and Apache Karaf console.
If you have any suggestions on where to look next, please let us know in the comments below!
Recommendations
PyYAML offers a safe_load method that uses the SafeLoader class, which is recommended in version 6.0 and later. This method only uses standard YAML tags and avoids constructing arbitrary Python objects. Using SafeLoader ensures you’re only reading data objects composed of Python primitives and basic data structures, which is usually sufficient. If you require state and behaviour, PyYAML can derive from the YAMLObject class to create safe, user-defined types recognisable by SafeLoader.
Remote Code Execution
AIT-Core comes with a server that enables the ZMQ topic/streams to make telemetry and telecommands packets available remotely, i.e., via API script. To start a simple server, importing an Instrument from the AIT-Core API and instantiating it is enough (see Figure 20).
The created instrument is now ready to subscribe to a telemetry source. For this purpose, we used the TLM module to create a Telemetry simulator, which retrieves a definition of a simple TM packet and sends it every 5 seconds (see Figure 21). In addition, to make the test more realistic, we have set up the TLM Instrument and TLM simulator on two different hosts. To exchange the telemetry, the AIT-Core opens a new port (5560 in our case), establishes the connection between the processes, and communicates using ZeroMQ messaging.
By monitoring the network traffic, we captured the TCP Frames exchanged between the instrument and the TLM simulator (see Figure 22). Given that the ZeroMQ communication used in the AIT-Core is unencrypted, we were able to capture the raw TCP Frames and analyse their content. The Python Pickle object serialisation/deserialisation library powers the TLM functionality. As per Python documentation, this library is insecure and allows bad actors to achieve Command Execution. Given that the communication is unencrypted, a bad actor can conduct a Man-in-the-Middle (MitM) attack to capture the raw TCP Frames and modify their content. With Pickle in the loop, this can lead to an RCE on either of the hosts in this communication link without access to any of them.
We have used our tool to conduct a MitM attack to demonstrate this. First, based on the previously captured TCP Frames, we reconstructed a valid TM packet, which, when encoded, looks like the data shown in Figure 23.
Knowing that once the TLM Instrument receives this data, it will try to load it into the Pickle library, we have developed the following exploitation scenario:
- Craft a payload that opens a reverse shell.
- Capture a TCP Frame that contains a valid ZMQ topic and TM packet data.
- Follow the same procedure as the TLM module to craft a new TCP Frame.
- Pickle the reverse shell payload twice (in the original procedure, AIT-Core includes the TM Packet UID, but for exploitation, it is unnecessary).
- Include the ZMQ topic.
- Replace the data field of the captured TCP Frame with the maliciously crafted one.
Figure 24 shows an example of a reverse shell payload that we have decided to use for this exploitation scenario.
After performing all required steps (encoding, dumping data with pickle, and adding the topic), the new TCP Frame data field looks like Figure 25.
Using our tool to conduct the MitM attacks at the TCP Frame level, we could replace a valid TCP Frame with one containing the reverse shell. Figure 26 shows the execution of our exploit. Figure 27 shows how to capture a valid exchange between the TLM instrument and the TLM simulator. The TLM Instrument receives a malicious payload (see Figure 28), which is then executed and opens a reverse shell to the attacker’s host (see Figure 29).
Recommendations
In the exploitation scenario described above, we see two following vulnerabilities:
- ZMQ communication is unencrypted.
- Use of Pickle
To prevent the RCE, we recommend resolving both issues. Although replacing the plain ZMQ communication with ZMQ SSH Tunnelling might be tempting, more is needed. It will mitigate the MitM attacks; however, given that the TLM instrument opens a port and connects to a telemetry source without any verification, another attack vector emerges for exploitation – in case the bad actor can access the telemetry source host, they can stop a telemetry source and start their own with a malicious payload.
Further readings
Suppose you’d like to learn more about spacecraft mission control software security. In that case, you can just read our articles on vulnerabilities in Open MCT (first and second) and YAMCS (first and second).
To highlight the importance of the software supply chain, we have further published exploits for the OSGi console and Apache Karaf console.
If you have any suggestions on where to look next, please let us know in the comments below!


