Your AppArmor profiles are enforced. Your containers are isolated. Your kernel is protected — or so you thought. On March 12, 2026, Qualys Threat Research Unit dropped a disclosure that quietly invalidated nine years of those assumptions.

TL;DR

  • Qualys TRU found 9 “confused deputy” vulnerabilities in Linux AppArmor’s kernel implementation, collectively named CrackArmor
  • Root cause: AppArmor’s pseudo-files (/sys/kernel/security/apparmor/.load, .replace, .remove) are world-writable (mode 0666), and trusted SUID binaries like Sudo and Postfix can be weaponized as proxies to load attacker-controlled profiles
  • Consequences: local privilege escalation (LPE) to root, container isolation collapse, KASLR bypass via out-of-bounds reads, and DoS via stack exhaustion
  • Affects all Linux kernels since 4.11 (2017) — Ubuntu, Debian, SUSE with AppArmor enabled by default — 12.6 million enterprise instances exposed
  • No CVE assigned yet; PoC withheld; patches are available — patch now

Why This Matters

AppArmor is not an optional hardening layer for most production Linux deployments. It ships enabled by default on Ubuntu, Debian, and SUSE. Docker applies AppArmor profiles to every container. Kubernetes uses it for pod-level confinement. This is the layer that security teams trust to contain compromised services, enforce least-privilege on containerized workloads, and prevent lateral movement after initial access.

CrackArmor doesn’t break AppArmor’s policy logic. It breaks the kernel mechanism that loads and enforces those policies. The attacker doesn’t need to craft a clever profile bypass — they manipulate the security subsystem itself.

If you’re running Ubuntu 22.04 LTS, 24.04 LTS, Debian 12, or any SUSE/openSUSE release, this is your environment. And it has been vulnerable since 2017.


Table of Contents


AppArmor Internals: How It Actually Works

AppArmor is a Linux Security Module (LSM) — a kernel-level framework that intercepts security-sensitive operations before they execute. Where DAC (Discretionary Access Control) asks “who owns this file?”, LSMs like AppArmor and SELinux ask “is this process allowed to do this operation at all?”

AppArmor works through profiles: per-process policy documents that define exactly what files, capabilities, network sockets, and system calls a process may access. A profile can be in one of two modes:

  • Enforce mode — violations are blocked and logged
  • Complain mode — violations are logged but permitted (used during profile development)

Profiles are loaded into the kernel at boot or runtime through a set of pseudo-files exposed under /sys/kernel/security/apparmor/:

/sys/kernel/security/apparmor/.load # Load a new profile
/sys/kernel/security/apparmor/.replace # Replace an existing profile
/sys/kernel/security/apparmor/.remove # Remove a profile by name
/sys/kernel/security/apparmor/.access # Query profile permissions

These files aren’t regular filesystem objects — they’re kernel interfaces that directly manipulate the LSM’s internal policy database. Writing a profile blob to .load is functionally equivalent to executing kernel policy code.

The apparmor_parser userspace tool is the standard way to interact with these files — it compiles human-readable profiles into a binary format and writes them to the kernel interface. But nothing stops a direct write() syscall to these paths.


The CrackArmor Research: What Qualys Found

Qualys TRU published their findings on March 12, 2026 after coordinated disclosure with the Linux kernel security team and major distributions. The research identifies nine distinct vulnerability classes in AppArmor’s kernel implementation — all tracing back to a single design oversight that has existed since Linux kernel 4.11, released in May 2017.

The name “CrackArmor” reflects the nature of the attack: not breaking the lock, but manipulating the mechanism that installs it.

Key facts from the disclosure:

FactorDetail
Discovered byQualys Threat Research Unit (TRU)
DisclosedMarch 12, 2026
Vulnerability age~9 years (since kernel 4.11, 2017)
CVE statusNot assigned (upstream process, ~1-2 weeks post-patch)
PoC statusWithheld by Qualys
Affected kernelsAll with AppArmor enabled since 4.11
Exposed systems12.6 million enterprise Linux instances
Primary distrosUbuntu, Debian, SUSE (AppArmor default-enabled)
Patch statusAvailable — Ubuntu confirmed fixes

The flaws reside in AppArmor’s kernel implementation — the C code inside the Linux kernel that processes profile data, not the profile syntax itself. This distinction is critical: profiles written correctly for AppArmor remain logically sound. The attack surface is the parsing and loading infrastructure, not the policy language.


Root Cause: The Confused Deputy Problem

The confused deputy problem is a classic privilege escalation pattern: a high-privilege process (the “deputy”) is tricked by a low-privilege attacker into performing a privileged operation on the attacker’s behalf.

In CrackArmor, the deputies are trusted SUID binaries like Sudo and Postfix. The mechanism is the AppArmor pseudo-file interface.

Here’s the fundamental issue: the three primary AppArmor profile pseudo-files are world-writable:

Terminal window
$ ls -la /sys/kernel/security/apparmor/.load
--w--w--w-- 1 root root 0 Mar 13 /sys/kernel/security/apparmor/.load

Mode 0666 — any unprivileged local user can open .load and .replace in O_WRONLY mode. What prevents an attacker from just writing an attacker-controlled profile directly? The kernel checks the process credentials at write time and enforces that only privileged processes can load profiles that modify existing confinement.

But here’s where the confused deputy comes in. When a privileged binary like sudo or postfix opens one of these pseudo-files as part of its normal operation — or can be coerced into doing so — the kernel sees the binary’s credentials, not the attacker’s. The attacker’s data travels through a trusted channel.

The nine vulnerabilities exploit variations of this pattern:

  1. Attacker opens the pseudo-file (permitted by 0666 mode)
  2. Attacker passes the open file descriptor to a privileged SUID binary via standard UNIX mechanisms (fd passing, symlinks, race conditions in temp file handling)
  3. Privileged binary writes attacker-controlled data to the kernel interface
  4. Kernel loads the malicious profile under the authority of the privileged process

The result: the attacker has loaded an AppArmor profile that grants unrestricted capabilities to an attacker-controlled process — bypassing the kernel protection that was supposed to prevent exactly this.


The Nine Vulnerability Classes

Qualys has not released full technical details for all nine flaws pending wider patch adoption. Based on the public advisory, they fall into these categories:

1. World-Writable Pseudo-File Interface

The foundational issue. Profile interface files expose write access to all users, enabling the subsequent attack classes.

2. SUID Binary Proxy via FD Passing

Open pseudo-file descriptors can be inherited or passed to SUID processes. The kernel evaluates credentials at the time of the privileged write, not at open time.

Race condition between temp file creation and kernel read — attacker substitutes a malicious profile blob in the window between creation and consumption by a privileged loader.

4. User Namespace Restriction Bypass

AppArmor enforces restrictions on what user namespaces (unprivileged containers) can do. The confused deputy path bypasses the namespace-level restriction checks, allowing a confined namespace to load profiles that should require host-level privileges.

5. Profile Replacement Without Authorization Check

The .replace interface lacks a consistent authorization check across all code paths. An attacker can replace an existing, restrictive profile with a permissive one for a target process without triggering the expected privilege verification.

6. Out-of-Bounds Read in Profile Parser

Malformed profile data causes the kernel parser to read beyond allocated buffer bounds. Depending on kernel memory layout, this leaks kernel addresses — enabling KASLR defeat and informing subsequent exploitation.

7. Stack Exhaustion via Recursive Profile Parsing

A specially crafted profile with deeply nested or circular references triggers unbounded recursion in the profile parser, causing kernel stack exhaustion — denial of service without any special privileges.

8. Arbitrary Code Execution via Profile Blob Injection

Once profile loading is achieved through a deputy, carefully constructed binary profile data can corrupt kernel memory structures. Combined with KASLR bypass (class 6), this enables arbitrary kernel code execution.

9. Permission Cache Poisoning

AppArmor caches permission decisions for performance. A race condition in cache invalidation allows an attacker to poison the cache with incorrect (permissive) entries that persist after a stricter profile is loaded.


Attack Chain: From Unprivileged User to Root

The realistic exploitation path for a threat actor with a local shell (post initial access, container escape attempt, or insider threat scenario):

[1] Reconnaissance
- Confirm AppArmor enabled: cat /sys/module/apparmor/parameters/enabled
- Identify kernel version: uname -r (target: >= 4.11)
- List loaded profiles: cat /sys/kernel/security/apparmor/profiles
- Check pseudo-file permissions: ls -la /sys/kernel/security/apparmor/
[2] Deputy Identification
- Find SUID binaries that interact with AppArmor or have predictable
file operation patterns: find / -perm -4000 2>/dev/null
- Target: sudo, postfix, or custom SUID binaries with file write behavior
[3] Pseudo-File FD Acquisition
- Open .load or .replace in O_WRONLY (permitted by 0666)
- Establish a position for fd-passing or race exploitation
[4] Deputy Execution
- Coerce target SUID binary into writing attacker-controlled profile data
through the held file descriptor
- The kernel sees trusted credentials; loads the profile
[5] Profile Activation
- Loaded profile grants CAP_SYS_ADMIN or unrestricted capabilities
to an attacker-controlled process
- Execute the privileged process under the new profile
[6] Root Shell
- Profile permits unrestricted syscalls and capability acquisition
- Escalate to UID 0

The critical insight for red teamers: this is a local privilege escalation vector. It requires an existing foothold with local user access. The realistic threat scenarios are:

  • Post-exploitation — after a web app RCE drops a shell as www-data, LPE to root via CrackArmor
  • Container escape — containerized attacker exploits user namespace restriction bypass to load host-level profiles
  • Insider threat — unprivileged employee escalates to root on a shared Linux workstation or build server
  • Cloud instance — exploiting any RCE in a hosted app to chain into full instance compromise

Container Isolation Bypass

This is the CrackArmor vector most relevant to modern infrastructure. Docker, LXC, and Kubernetes all rely on AppArmor profiles as part of their container isolation stack. Docker’s default docker-default profile blocks a significant portion of dangerous syscalls and capabilities for contained processes.

The user namespace restriction bypass (class 4) is particularly severe in container contexts. Unprivileged containers operate within their own user namespace — they appear to have root inside the container but are mapped to an unprivileged UID on the host. AppArmor is supposed to enforce that even apparent-root inside a container cannot modify host-level profiles.

CrackArmor breaks this guarantee.

Exploitation path in a containerized environment:

[1] Attacker has code execution inside a container (e.g., via app vulnerability)
[2] User namespace restriction bypass allows writing to host AppArmor interface
from within the container's namespace context
[3] Attacker loads a custom profile that grants the container process
CAP_SYS_ADMIN on the host
[4] Container process escapes to host namespace
[5] Full host compromise

The impact extends to Kubernetes deployments. Many Kubernetes configurations apply AppArmor profiles via pod annotations. The assumption that AppArmor provides a meaningful security boundary for untrusted workloads is invalidated on unpatched kernels.


KASLR Bypass and Stack Exhaustion

Two of the nine classes target the kernel hardening layer directly rather than profile loading.

KASLR Bypass via Out-of-Bounds Read

Kernel Address Space Layout Randomization (KASLR) randomizes where the kernel loads in memory at boot, making it harder for exploits to predict the location of specific kernel functions or data structures. Modern kernel exploits that require RIP control or structure overwrites first need to defeat KASLR.

The AppArmor profile parser out-of-bounds read (class 6) leaks kernel pointer values when parsing malformed profile data. An attacker submits a crafted profile blob that triggers the OOB read, captures the output (visible through the return value or side-channel), and derives the kernel base address. This reduces KASLR from a hard barrier to a one-shot information disclosure.

Combined with the arbitrary code execution class (8), KASLR bypass completes the full kernel exploitation chain:

OOB read → kernel base address → calculate target offsets → overwrite kernel structure → root

Stack Exhaustion as DoS

Less exciting than root escalation but operationally relevant: any unprivileged user can craft a recursive profile that causes the kernel parser to recurse without bound, consuming the kernel stack until it hits the guard page.

The result is an unrecoverable kernel panic — system down, immediate reboot required. No authentication needed. Against any unpatched system with AppArmor enabled, this is a trivial denial-of-service primitive.


Detection Opportunities

Qualys is withholding PoC exploits, but detection engineering doesn’t require a working exploit. The attack chain has observable artifacts at multiple points.

Audit Log Monitoring

AppArmor writes to the kernel audit subsystem. Enable detailed auditing and watch for:

Terminal window
# Monitor AppArmor pseudo-file access
auditctl -w /sys/kernel/security/apparmor/.load -p w -k apparmor_profile_load
auditctl -w /sys/kernel/security/apparmor/.replace -p w -k apparmor_profile_replace
auditctl -w /sys/kernel/security/apparmor/.remove -p w -k apparmor_profile_remove

Any write to these files outside of expected management processes (apparmor_parser, system init scripts) is anomalous. The audit record captures UID, PID, and the calling binary — the confused deputy pattern shows a SUID binary writing on behalf of an unexpected caller.

Sigma Rule — Profile Modification by Unexpected Process

title: AppArmor Profile Modified by Unexpected Process
id: a9f3b2e1-7c44-4d80-9e12-3fa850d6b1c7
status: experimental
description: Detects writes to AppArmor kernel pseudo-files from processes other
than known management tools
logsource:
product: linux
service: auditd
detection:
selection:
type: SYSCALL
syscall: write
key: apparmor_profile_load
filter_legitimate:
exe|endswith:
- '/apparmor_parser'
- '/aa-exec'
- '/systemd'
condition: selection and not filter_legitimate
falsepositives:
- Custom AppArmor management scripts — add to filter
- Configuration management (Ansible, Puppet) — add to filter
level: high
tags:
- attack.privilege_escalation
- attack.T1548

Profile Inventory Baselining

Maintain a baseline of loaded profiles and alert on additions or replacements:

Terminal window
# Capture current profile state
cat /sys/kernel/security/apparmor/profiles | sort > /var/lib/security/apparmor_baseline.txt
# Compare at interval (cron or systemd timer)
diff /var/lib/security/apparmor_baseline.txt \
<(cat /sys/kernel/security/apparmor/profiles | sort)

Any delta outside a change window is a detection signal.

eBPF-Based Monitoring

For environments with modern kernels and eBPF tooling, hook the security_profile_load and security_profile_replace LSM hooks directly:

// Pseudo-code — attach to LSM hook
SEC("lsm/bpf")
int BPF_PROG(apparmor_profile_load_hook, ...)
{
// Log caller credentials, pid, and profile data hash
// Alert if caller is not in allowlist
}

Tools like Falco with its kernel module or eBPF driver can be configured with custom rules against AppArmor pseudo-file access without writing kernel code directly.

Kernel Panic Correlation

Stack exhaustion manifests as a sudden kernel panic with a stack trace involving apparmor_ functions. If you see:

BUG: kernel stack overflow
Call Trace:
apparmor_parse_rules+...
aa_unpack+...
aa_replace_profiles+...

That’s the DoS variant being triggered — whether accidental or intentional.


What You Can Do Today

1. Patch — This Is Mandatory

Ubuntu has published fixes. Patch immediately:

Terminal window
# Ubuntu / Debian
sudo apt update && sudo apt dist-upgrade
# Verify kernel version post-patch
uname -r
# SUSE / openSUSE
sudo zypper refresh && sudo zypper update kernel-default

Check Ubuntu’s security advisories for the specific kernel version that includes the fix for your release (20.04, 22.04, 24.04).

2. Harden the Pseudo-File Interface

While patching, restrict access to the AppArmor pseudo-files. The world-writable permission is the root cause — tighten it:

Terminal window
# Restrict write access to root only
# NOTE: This may break AppArmor management tools running as non-root
# Test in non-production first
chmod 0200 /sys/kernel/security/apparmor/.load
chmod 0200 /sys/kernel/security/apparmor/.replace
chmod 0200 /sys/kernel/security/apparmor/.remove

A more robust approach is to use a systemd-tmpfiles rule to enforce permissions at boot:

/etc/tmpfiles.d/apparmor-restrict.conf
m /sys/kernel/security/apparmor/.load 0200 root root -
m /sys/kernel/security/apparmor/.replace 0200 root root -
m /sys/kernel/security/apparmor/.remove 0200 root root -

3. Deploy Detection Rules

Implement the auditd rules from the detection section immediately. They have near-zero performance impact and give you visibility into any exploitation attempts before or after patching.

4. Review Container Security Posture

Audit your Docker and Kubernetes deployments:

Terminal window
# Check which AppArmor profiles are applied to running containers
docker inspect --format='{{.HostConfig.SecurityOpt}}' <container>
# Kubernetes — check pod security context
kubectl get pod <pod> -o jsonpath='{.metadata.annotations}'

Any container running with --security-opt apparmor=unconfined is already outside AppArmor’s protection. Any container on an unpatched host is now also at risk even with profiles applied.

5. Assess Exposure Surface

Identify systems where AppArmor is enabled and local access exists for untrusted users:

  • Shared Linux workstations
  • Build servers with developer access
  • Any system running containerized multi-tenant workloads
  • Cloud VMs where application-layer RCE could chain to LPE

Priority patching order: container hosts > shared systems > single-tenant VMs with no local untrusted access.

6. Monitor for Exploitation Attempts

Set up alerts in your SIEM for:

  • AppArmor profile writes from unexpected processes
  • New profiles loaded outside change windows
  • Kernel panics with AppArmor stack traces
  • SUID binary execution followed by kernel interface access (behavioral correlation)


Sources