Bug 2544510 (CVE-2026-103754)
| Summary: | CVE-2026-103754 ansible-runner: ansible-runner: path traversal and symlink escape in unstream_dir() allows file write outside the target directory | ||
|---|---|---|---|
| Product: | [Other] Security Response | Reporter: | OSIDB Bzimport <bzimport> |
| Component: | vulnerability | Assignee: | Product Security <prodsec-ir-bot> |
| Status: | NEW --- | QA Contact: | |
| Severity: | medium | Docs Contact: | |
| Priority: | medium | ||
| Version: | unspecified | CC: | dschmidt, ikhan, jlanda, kshier, rbobbitt, simaishi, stcannon, yguenane |
| Target Milestone: | --- | Keywords: | Security |
| Target Release: | --- | ||
| Hardware: | All | ||
| OS: | Linux | ||
| Whiteboard: | |||
| Fixed In Version: | Doc Type: | --- | |
| Doc Text: |
A flaw was found in ansible-runner. The unstream_dir() function, which receives and extracts a streamed zip archive on the worker side of the ansible-runner transmit/worker protocol, re-creates symbolic links from archive content without validating the link target and applies chmod() and utime() to an unsanitized filesystem path derived from the archive member name. A crafted archive processed by a worker that consumes attacker-influenced input can create files, create symbolic links, or change permissions outside the intended target directory, which can be leveraged toward code execution.
|
Story Points: | --- |
| Clone Of: | Environment: | ||
| Last Closed: | Type: | --- | |
| Regression: | --- | Mount Type: | --- |
| Documentation: | --- | CRM: | |
| Verified Versions: | Category: | --- | |
| oVirt Team: | --- | RHEL 7.3 requirements from Atomic Host: | |
| Cloudforms Team: | --- | Target Upstream Version: | |
| Embargoed: | |||
Reported via the ansible-security list by Liqiang Ji (ISCAS / SQUARE Research Group). Module: src/ansible_runner/utils/streaming.py, function unstream_dir(). unstream_dir() performs a custom ("fancy") extraction to preserve permissions and symlinks. Two defects: 1. CWE-59 (link following): a symlink member's target is read verbatim from the archive content and re-created with os.symlink() without validation, so it can point outside target_directory (e.g. '../outside' or an absolute path). A subsequent member extracted through that symlink lands outside target_directory (arbitrary write escape). 2. CWE-22 (path traversal): out_path = os.path.join(target_directory, info.filename) is built from the unsanitized member name. ZipFile.extract() sanitizes the arcname, but os.utime()/os.chmod() operate on the raw out_path, so a member named '../victim' changes mtime/permissions on a file outside target_directory. Exploitability: in the standard AWX/AAP topology the transmit stream is produced by the trusted controller (stream_dir only encodes pre-existing symlinks) and the controller->executor path is authenticated, so there this is defense-in-depth hardening. It is directly relevant to deployments that feed untrusted input into ansible-runner's Worker() path. Upstream: https://github.com/ansible/ansible-runner Fix PR: https://github.com/ansible/ansible-runner/pull/1550 Confirmed present in 2.4.3 and devel.