CrackArmor LPE via Confused-Deputy and Sudo/Postfix

Within the CrackArmor suite, a subset of two vulnerabilities can be combined to achieve local root privileges. This chain exploits a confused-deputy flaw in AppArmor profile management and a fail-open privilege-handling behavior in sudo when Postfix is installed.

An unprivileged user can manipulate AppArmor’s pseudo-files to load a crafted profile denying CAP_SETUID to sudo. This causes sudo to fail to drop its root privileges when invoking the mailer. By supplying a controlled Postfix configuration via the MAIL_CONFIG environment variable, the user can execute a helper binary with full root privileges, achieving reliable local privilege escalation.

This issue affects Linux kernels starting from 4.1.1 (released in 2017), and patches addressing the AppArmor interface flaws appear in 6.8.0-106.


Technical Details

The attack begins with AppArmor’s pseudo-files (.load, .replace, .remove) in /sys/kernel/security/apparmor. Although write() operations are restricted, the files themselves are world-writable at the file descriptor level. This allows an unprivileged user to trick a privileged process, such as su -P, into writing fully controlled content—a classic confused-deputy scenario. Using this primitive, the attacker injects a malicious AppArmor profile for sudo that explicitly denies CAP_SETUID.

When sudo executes under this profile, its calls to setuid() and setresuid() fail, but sudo continues running instead of aborting. In error handling, sudo invokes Postfix’s /usr/sbin/sendmail using the original environment, which can be controlled via MAIL_CONFIG. By pointing this variable to an attacker-controlled directory containing malicious helper binaries, the mailer executes these binaries as root, completing the privilege escalation.


PoC || GTFO

void@ubnt-dev:~$ python3 crackarmor.py 
--=== CrackArmor LPE ===--
[+] Setting up payload (local)
[+] Building profile
[+] Injecting profile
Password: 
[+] Triggering exploit

========== IMPORTANT ==========
[!] SUID shell: /tmp/rootbash
[!] sudo is broken (AppArmor profile replaced)
[!] Restore with: python3 script.py --restore
===============================

rootbash-5.2# id
uid=1000(void) gid=1000(void) euid=0(root) groups=1000(void)

Last updated