GitHub Advisoryadvisory · PyPI

GHSA-p462-prxw-mjx4 NASA AMMOS Instrument Toolkit: Path traversal resulting in arbitrary file append (can be triggered over the network by unauthenticated attacker)

CRITICAL Published 2026-06-05 Modified 2026-06-05 Official page ↗
Aliases
CVE-2026-47731
Ecosystem
PyPI
Packages
ait-core
Severity
Critical
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H
Weaknesses
CWE-22
Reviewed
2026-06-05

Affected packages

• PyPI ait-core: >= 3.1.0, < 3.1.1 → fixed in 3.1.1 • PyPI ait-core: < 2.6.1 → fixed in 2.6.1

Description

1. Summary The Binary Stream Capture (BSC) component exposes an unauthenticated HTTP API for dynamically creating packet capture “handlers.” Because the code blindly trusts path‑related form fields, a remote client can: • Bypass the configured log root and direct BSC to log to arbitrary filesystem paths (path traversal / directory escape), and • Append attacker‑controlled data to those files, using the privileges of the`ait-bsc` process. There are two ways for a remote attacker to trigger this: 1. If the attacker has access to the network where `ait-bsc` is deployed (a reason for that could be that the ports are publicly accessible), the payloads can be directly sent to the server to trigger the arbitrary file append. This type of attack is demonstrated in `python_poc.py`. 2. Even if the attacker does not have direct access to the network because the software is running in a local network, it is possible to exploit this if a bad actor in that network opens an attacker-controlled website (which might be a website created by an attacker, or a third-party website compromised by the attacker). The browser javascript can automatically send the requests necessary to exploit this into the local network. This is even possible if the server is only accessible on `localhost`. This type of attack is demonstrated by `attacker_tcp.py` and `test1.html` (first launch the attacker TCP server, then start a webserver to host `test1.html`, for example using `python3 -m http.server 7000`, and open `test1.html`). Impact This issue affects BSC (Binary Stream Capture) and usage of the ait-bsc server. This impacts AIT-Core versions before 3.1.1, from 2.x before 2.6.1. Users are recommended to upgrade to version 3.1.1 or 2.6.1. Details A remote attacker can use this vulnerability to append data to arbitrary files on the system (if the `ait-bsc` has privileges to write to them). It is easy to use this to corrupt data on the system (which can include the AIT-Core python code to crash the server after it is restarted and python attempts to execute the corrupted code). It should be mentioned here that there seems to be a bug in the TCP handler that results in a lot of data being written in an infinite loop after the connection has been closed, this could result in excessive disk space use. That the attacker can modify executable files like python or bash scripts means that this vulnerability could also lead to Remote Code Execution as soon as the user runs the modified code. However, depending on the system, it is not so easy to execute this attack in practice (it might not be possible), because `ait-bsc` adds a header in front of attacker-controlled data. Fix Information The vulnerability is mitigated by constraining BSC ability to write paths only in the project root log directory which is configured through the bsc.yaml. Additionally, any attempts to traverse outside of the configured location are rejected. Patches • 3.1.1 • 2.6.1 --- 2. Affected Code Paths 2.1 REST entry point: `/NAME/start` `StreamCaptureManagerServer` exposes an unauthenticated POST endpoint: ```python ait/core/bsc.py class StreamCaptureManagerServer(Bottle): def _route(self): self._app.route("/", method="GET", callback=self._get_logger_list) self._app.route("/stats", method="GET", callback=self._fetch_handler_stats) self._app.route( "//start", method="POST", callback=self._add_logger_by_name ) self._app.route( "//stop", method="DELETE", callback=self._stop_logger_by_name ) ... ``` Handler: ```python def _add_logger_by_name(self, name): ... data = dict(request.forms) loc = data.pop("loc", "") port = data.pop("port", None) conn_type = data.pop("conn_type", None) if not port or not conn_type: raise ValueError("Port and/or conn_type not set") address = [loc, int(port)] if "rotate_log" in data: data["rotate_log"] = True if data == "true" else False if "rotate_log_delta" in data: data["rotate_log_delta"] = int(data["rotate_log_delta"]) self._logger_manager.add_logger(name, address, conn_type, data) ``` All form fields except `loc`, `port`, `conn_type` are passed directly as `data` into add_logger. This includes attacker‑controlled `path`, `file_name_pattern`, and potentially `log_dir_path`. There is no authentication on this route. 2.2 Manager: unvalidated `path` and `log_dir_path` `StreamCaptureManager.add_logger`: ```python ait/core/bsc.py def add_logger(self, name, address, conn_type, log_dir_path=None, kwargs): capture_handler_conf = kwargs if not log_dir_path: log_dir_path = self._mngr_conf["root_log_directory"] log_dir_path = os.path.normpath(os.path.expanduser(log_dir_path)) capture_handler_conf["log_dir"] = log_dir_path capture_handler_conf["name"] = name if "rotate_log" not in capture_handler_conf: capture_handler_conf["rotate_log"] = True ... address_key = str(address) if address_key in self._stream_capturers: capturer = self._stream_capturers[address_key][0] capturer.add_handler(capture_handler_conf) return socket_logger = SocketStreamCapturer(capture_handler_conf, address, conn_type) greenlet = gevent.spawn(socket_logger.socket_monitor_loop) self._stream_capturers[address_key] = (socket_logger, greenlet) self._pool.add(greenlet) ``` Key points: • If the REST client supplies `log_dir_path` explicitly (as a named parameter), it overrides the manager’s `root_log_directory`. • All other attacker‑supplied fields in `kwargs` become part of the handler configuration dict (`capture_handler_conf`), including `path` and `file_name_pattern`. • There is no check that `log_dir_path` or `path` are relative or confined. 2.3 Path traversal via `_get_log_file` `SocketStreamCapturer._get_log_file` builds the actual log path: ```python ait/core/bsc.py def _get_log_file(self, handler): """Generate log file path for a given handler""" if "file_name_pattern" not in handler: filename = "%Y-%m-%d-%H-%M-%S-{name}.pcap" else: filename = handler["file_name_pattern"] log_file = handler["log_dir"] if "path" in handler: log_file = os.path.join(log_file, handler["path"], filename) else: log_file = os.path.join(log_file, filename) log_file = time.strftime(log_file, time.gmtime()) log_file = log_file.format(handler) return log_file ``` On POSIX systems: • If `handler["path"]` is absolute (e.g. `/home/user/...`), `os.path.join(base, abs_component, ...)` discards the `base` component: ```python os.path.join("/configured/root", "/home/elias/ait-venv/...", "dmc.py") # -> "/home/elias/ait-venv/.../dmc.py" ``` • If `handler["path"]` contains `..`, the result can point outside the nominal root even if `path` is relative. There is: • No `os.path.realpath` canonicalization *after* join, and • No enforcement that the final `log_file` begins with the configured root prefix. Combined with `StreamCaptureManager.add_logger`, this means: • Attacker controls both: • `handler["log_dir"]` (via `log_dir_path`), and • `handler["path"]` and `handler["file_name_pattern"]`. They can therefore direct BSC’s log output to any path that the OS permissions allow, not just under `root_log_directory`. 2.4 File opened for append without safety checks ```python ait/core/bsc.py def _get_logger(self, handler): """Initialize a PCAP stream for logging data""" log_file = self._get_log_file(handler) if not os.path.isdir(os.path.dirname(log_file)): os.makedirs(os.path.dirname(log_file)) handler["log_rot_time"] = time.gmtime() return pcap.open(log_file, mode="a") ``` `pcap.open`: ```python ait/core/pcap.py def open(filename, mode="r", **options): ... mode = mode.replace("b", "") + "b" # "a" -> "ab" ... s…

Related

Alias · CVE (1)

  • CVE-2026-47731 The AMMOS Instrument Toolkit (Formerly the Bespoke Links to Instruments for Surface and Space (BLISS)) is a Python-based software suite… vulnerability

Weakness · CWE (1)

  • CWE-22 Improper Limitation of a Pathname to a Restricted Directory weakness

References