Skip to content Skip to footer

Remote Code Execution via Man-in-the-Middle (and more) in NASA’s AIT-Core v2.5.2

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:

Watch the RCE via MitM demo in action:

https://www.linkedin.com/embeds/publishingEmbed.html?articleId=8415846862397090808

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.

Figure 1: SQLi in query_packets of InfluxDB implementation.
Figure 2: SQLi in query_packets of SQLite implementation.

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).

Figure 3: Configuration of tlm.yaml that contains a malicious packet definition.
Figure 4: An example of exploitation is calling the query_packets function on the SQLite back end.

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.

Figure 5: Partially secure SQL query build implementation.

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.

Figure 6: eval occurrence in the API
Figure 7: eval occurrence in TLM packet expression.

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: Call the wait function with a Python payload.

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.

Figure 9: Code Execution via wait API function.

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: An example of a malicious packet expression.

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 11: Test case to demonstrate the Local Code Execution.

Figure 12 demonstrates the execution of the above test case, showing how the earlier expression allows for achieving the Local Code Execution.

Figure 12: Demonstration of Local Code Execution via TLM Packet Expression.

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).

Figure 13: AIT-Core processes the leapseconds.dat using Pickle.

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).

Figure 14: Example of a malicious Pickle file.
Figure 15: AIT-Core DMC loads the malicious Pickle file.

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.

Figure 16: Example of a malicious YAML 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).

Figure 17: Test script that loads TM definitions.

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).

Figure 18: Code Execution by trying to load TM definitions.

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!

Figure 19: Other areas of

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).

Figure 20: Creation of AIT-Core server.

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.

Figure 21: AIT-Core TM simulator.
Figure 22: Communication between the instrument and TM simulator.

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.

Figure 23: Valid TM Packet.

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.

Figure 24: Example of a reverse shell payload.

After performing all required steps (encoding, dumping data with pickle, and adding the topic), the new TCP Frame data field looks like Figure 25.

Figure 25: TCP Frame containing a malicious payload.

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).

Figure 26: Exploit execution.
Figure 27: Capturing a valid TM Packet.
Figure 28: TLM Instrument receives a malicious packet.
Figure 29: Reverse shell from a TLM Instrument to the attacker’s host.

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!