CVE-2026-55074 Overview
CVE-2026-55074 is a symlink-following vulnerability in the Ansible FreeBSD Jail Connection Plugin (jailexec), an Ansible connection plugin that manages FreeBSD jails via jexec. Through version 1.3.0, the plugin's put_file implementation resolved transfer destinations to paths on the jail host and executed mkdir -p and mv as root outside the jail. Because those commands follow symbolic links, an attacker inside a managed jail can plant a symlink that redirects the root-privileged host-side write to an arbitrary location on the host filesystem. The result is a full jail escape from a low-privileged foothold to root on the FreeBSD host. The issue is fixed in version 2.0.0.
Critical Impact
An attacker with the ability to create symlinks inside a managed FreeBSD jail can trigger arbitrary root-owned file writes on the host, leading to complete host compromise via cron, rc.d, or authorized_keys overwrites.
Affected Products
- Ansible FreeBSD Jail Connection Plugin (ansible_jailexec) versions up to and including 1.3.0
- Ansible control nodes using the jailexec connection plugin against untrusted or multi-tenant jails
- FreeBSD hosts running jails managed by vulnerable versions of the plugin
Discovery Timeline
- 2026-09-21 - CVE-2026-55074 published to NVD
- 2026-09-24 - Last updated in NVD database
Technical Details for CVE-2026-55074
Vulnerability Analysis
The jailexec connection plugin transfers files into a jail by first computing the destination path as a concatenation of the jail's root path on the host and the in-jail target path. It then runs mkdir -p and mv against that concatenated path directly on the host, executing as root. Neither command uses -h, realpath validation, or O_NOFOLLOW-equivalent semantics, so both traverse symbolic links present in the destination hierarchy. This is a classic link-following file operation flaw categorized as [CWE-59].
Because the file operations occur on the host rather than inside the jail context, a symlink placed inside the jail is interpreted by the host's root shell against the host's global filesystem namespace. An attacker who controls content in the jail can therefore convert a legitimate copy/template/fetch operation into an arbitrary root-owned write anywhere on the host.
Root Cause
The root cause is that put_file performs privileged filesystem mutation outside the jail's confinement while trusting path components that are attacker-controlled. mkdir -p walks and creates directories following any intermediate symlinks, and mv writes the transferred payload to the resolved target. The plugin never re-validates that the final resolved path remains inside the jail root before executing the privileged operation.
Attack Vector
Exploitation requires two preconditions. First, an operator must run an Ansible task using put_file semantics (copy, template, or fetch) that targets a path inside the jail. Second, the attacker must be able to create a symlink inside the jail at or above the task's destination before the transfer runs. The attacker can be the jail's root user or any process able to write a symlink into a directory the playbook later writes to. Post-exploitation typically involves overwriting /etc/crontab, files under /usr/local/etc/rc.d/, or ~/.ssh/authorized_keys on the host to obtain persistent root access.
No synthetic exploitation code is included; see the GitHub Security Advisory GHSA-cxgv-hp74-jj7r for the maintainer's technical write-up.
Detection Methods for CVE-2026-55074
Indicators of Compromise
- Unexpected symbolic links inside managed jails whose targets point to sensitive host paths such as /etc/, /usr/local/etc/rc.d/, /var/cron/tabs/, or /root/.ssh/
- Root-owned files on the FreeBSD host created or modified during Ansible playbook runs with modification timestamps aligned to jailexec task execution
- Ansible logs showing copy, template, or fetch tasks against jails succeeding at unexpected host-side paths
Detection Strategies
- Audit installed versions of ansible_jailexec on all Ansible control nodes and flag any release at or below 1.3.0
- Review historical playbook logs for put_file operations targeting jails that host untrusted workloads or non-administrator users
- Compare pre- and post-run filesystem snapshots of the FreeBSD host to detect writes outside expected jail root paths
Monitoring Recommendations
- Enable FreeBSD auditd rules for open, rename, and mkdirat syscalls executed by the Ansible SSH user, and alert on paths outside declared jail roots
- Forward FreeBSD audit logs and Ansible run logs into a centralized log platform for correlation of jail-boundary crossings
- Monitor changes to high-value host files such as authorized_keys, crontab, and rc.d scripts and alert on modifications occurring during automation windows
How to Mitigate CVE-2026-55074
Immediate Actions Required
- Upgrade ansible_jailexec to version 2.0.0 or later on every Ansible control node that manages FreeBSD jails
- Pause copy, template, and fetch tasks targeting jails that host untrusted tenants until the upgrade is complete
- Inspect managed jails for pre-planted symlinks pointing outside the jail root and remove any that are not explicitly required
Patch Information
The vulnerability is fixed in Ansible FreeBSD Jail Connection Plugin v2.0.0. The remediation is tracked in Pull Request #6 and landed via commit 6e80eec. The maintainer's advisory is available at advisory-2.0.0-jail-escape.md.
The patch commit also tightens repository hygiene to prevent leaking test inventories with real host credentials:
*.retry
hosts
inventory
-test-inventory
+# Local integration-test inventories contain real host addresses/credentials;
+# only the .example template belongs in the repo.
+test-inventory*
+!test-inventory.ini.example
# Local ansible.cfg used to point Ansible at this checkout for dev/testing.
ansible.cfg
# Source: https://github.com/chofstede/ansible_jailexec/commit/6e80eecb2db7aebd1258fded6ae5c3aba1dd4123
include LICENSE
include requirements-test.txt
include pytest.ini
-recursive-include tests *.py *.yml *.ini *.example
+recursive-include tests *.py *.yml *.example
recursive-include tests/integration *.md
global-exclude __pycache__
global-exclude *.py[cod]
+# Never ship local integration inventories (they hold real hosts/credentials);
+# only the committed *.example template belongs in the sdist.
+global-exclude test-inventory.ini
+prune dist
+prune build
+prune htmlcov
+prune .venv
# Source: https://github.com/chofstede/ansible_jailexec/commit/6e80eecb2db7aebd1258fded6ae5c3aba1dd4123
Workarounds
- Restrict use of the jailexec connection plugin to jails where all root-capable processes are fully trusted until the upgrade is applied
- Avoid copy, template, and fetch tasks that write into jail directories writable by non-administrative jail users
- Pre-validate jail destination paths from the host by resolving them with realpath and rejecting any that escape the jail root before invoking playbooks
# Upgrade the plugin to the fixed release
pip install --upgrade 'ansible_jailexec>=2.0.0'
# Verify installed version on the Ansible control node
python -c "import importlib.metadata as m; print(m.version('ansible_jailexec'))"
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

