Module 04 — Exploit Mitigation & Allowlisting¶
Type 7 · Build-&-Operate — build, tune, and enforce an AppArmor profile (complain → enforce) that confines a process so access to sensitive paths is denied and logged, delivering an operating allowlisting control. (Secondary: Audit→Build→Verify — you verify the control works by watching the confined process get denied.) Go to the hands-on lab →
Last reviewed: 2026-06
[Track 07 — Endpoint & Host Hardening] — Exploit mitigations raise the cost of attacks that bypass the hardening baseline; allowlisting raises the cost to near-infinite for a specific class.
In 60 seconds
Hardening shrinks attack surface, but it doesn't stop a zero-day in a service you must run. Exploit mitigations (ASLR, NX/DEP, stack canaries) are the next ring — they don't prevent vulnerabilities, they break the exploitation techniques that turn a bug into code execution. Application allowlisting (AppArmor, fapolicyd) is the ring after that: it constrains what an exploited process can do at all. This module builds and enforces an AppArmor profile the safe way — complain mode first, refine from real denials, then enforce.
Why this matters¶
Hardening reduces attack surface — fewer services running, stronger authentication, tighter permissions. But it doesn't stop a zero-day in a service you need to run. The 2017 WannaCry outbreak spread via EternalBlue (CVE-2017-0144, CVSS 9.3) — a memory-corruption flaw in Windows SMBv1 that let crafted packets execute arbitrary code with no authentication. Exactly the class of bug that mitigations like DEP/NX and ASLR exist to blunt: even with the vulnerability present, a non-executable heap and randomised memory layout make the exploit's crafted payload far less likely to land reliably. Exploit mitigations and application allowlisting are the second and third rings of defense: they make exploitation harder even when a vulnerability exists, and they prevent execution of code that wasn't explicitly approved. Together with the hardening baseline (modules 02–03), they form the layered posture that makes a host expensive enough to attack that adversaries move on.
Objective¶
Configure and demonstrate AppArmor profile enforcement on a Linux host: confine a process so that access to sensitive paths is denied and the denial is logged, showing the control working in practice.
The core idea¶
Exploit mitigations are kernel and compiler-enforced constraints that degrade the reliability of exploitation techniques. ASLR (Address Space Layout Randomisation), covered in module 03, randomises memory layout so that hardcoded addresses in exploits fail. NX/DEP (No-Execute / Data Execution Prevention) marks data pages as non-executable, so shellcode injected into a buffer cannot be executed directly — the attacker must resort to return-oriented programming (ROP), which is harder to construct and easier to detect. Stack canaries (compiler-inserted sentinel values) detect stack-smashing before the return address is overwritten. Together these mitigations form the baseline that makes memory-corruption exploits unreliable rather than impossible. They do not prevent vulnerabilities; they break common exploitation techniques.
The mental model
Three rings of cost. Hardening shrinks what's exposed; exploit mitigations (ASLR, NX/DEP, canaries) break the technique that turns a present bug into reliable code execution; allowlisting (AppArmor MAC profiles, fapolicyd trust rules) constrains what an exploited process is even allowed to do. None prevents the vulnerability — they raise the price of using it.
flowchart LR
A(["Attacker + a present bug"]) --> R1
R1["<b>Hardening</b><br/>shrink attack surface"] --> R2
R2["<b>Exploit mitigations</b><br/>ASLR · NX/DEP · canaries<br/>break the exploit technique"] --> R3
R3["<b>Allowlisting</b><br/>AppArmor · fapolicyd<br/>constrain the exploited process"] --> G(["code execution + impact"])
Application allowlisting operates at a different level. Instead of making exploitation harder, it prevents unauthorised code from running at all. On Linux, fapolicyd (Fedora/RHEL) and AppArmor (Ubuntu/Debian) implement this at the kernel level. fapolicyd uses an allow/deny rule set based on file properties (SHA-256, path, trust anchor from the RPM database); AppArmor uses mandatory access control (MAC) profiles that define per-application path, capability, and network permissions. Both enforce before execution — an attacker who exploits a service can only do what that service's profile allows.
AppArmor profiles have two modes: enforce and complain. In complain mode, violations are logged but not blocked — this is how you generate the baseline for a new profile without breaking the application. In enforce mode, violations are denied and logged. The operational pattern is: deploy in complain, run through normal application behaviour for a period, review the logs, tighten the profile, switch to enforce. A profile written by hand and enforced without a complain phase will break the application on first contact with a code path the author didn't anticipate.
The gotcha
"We have AppArmor installed" is not "AppArmor is confining anything." Ubuntu ships it enabled, but
most profiles are in complain mode or cover only a few system binaries — aa-status finding your
critical app running unconfined is a common, real finding. And enforcing a hand-written profile
with no complain phase breaks the app the first time it hits a path the author didn't anticipate.
The practitioner gap between "we have AppArmor installed" and "AppArmor is actually constraining anything" is enormous. Ubuntu ships AppArmor enabled by default, but most profiles are either in complain mode or cover only a small set of system applications. Checking aa-status on a production Ubuntu host and finding that your critical application is running unconfined is a common security finding. The remediation is straightforward — write or adopt an existing AppArmor profile, test in complain, enforce — but it requires someone to actually do it, which is the skill this module builds.
AI caveat
A model writes a plausible AppArmor profile skeleton fast — but it can't know every path your app
touches, so an enforced AI profile will break on the first unanticipated code path. Syntax-check
with apparmor_parser -p, run in complain mode, and refine from real aa-logprof denials.
Learn (~4 hrs)¶
Exploit mitigations — the mechanisms - Microsoft Learn — "Exploit protection reference" — a per-mitigation reference for DEP, ASLR (force-relocate / bottom-up), CFG, SEHOP, and the heap/stack protections; read each mitigation's "Description" to understand the specific memory-corruption step it disrupts and what it costs in compatibility.
AppArmor
- AppArmor wiki — User documentation — read the "Getting started" and "Policy language" sections to understand profile syntax.
- Ubuntu AppArmor documentation — practical guide including aa-genprof, aa-logprof, complain/enforce mode, and common profile patterns.
fapolicyd - fapolicyd — GitHub project documentation — the reference implementation and documentation for the trust-based allowlisting model; understand how fapolicyd differs from AppArmor's path-based approach through the rule examples.
Application allowlisting strategy - CISA — Known Exploited Vulnerabilities Catalog — scan this for how many KEVs target applications that allowlisting would block post-exploitation; context for why the control matters.
A mitigation's worth, in one CVE - NVD — CVE-2017-0144 (EternalBlue) — the SMBv1 RCE behind WannaCry/NotPetya; read the description and CWE to see the memory-corruption mechanism that DEP/NX and ASLR are designed to disrupt (and why a single unpatched, unmitigated host became a worm vector).
Key concepts¶
- ASLR, NX/DEP, and stack canaries are complementary mitigations that each break different steps in the exploitation chain.
- AppArmor enforces per-application MAC profiles; complain mode → enforce mode is the safe deployment path.
aa-statustells you which applications are confined and which are running unconfined — the common gap.- fapolicyd uses a trust anchor (RPM database or explicit trust list) to allow/deny execution by file hash.
- Allowlisting doesn't prevent vulnerabilities; it constrains what an exploited process can do post-compromise.
- EternalBlue (CVE-2017-0144) is the canonical memory-corruption RCE that DEP/NX and ASLR are built to degrade.
AI acceleration¶
Ask an AI to generate an AppArmor profile for a specific application (e.g. nginx, python3). The output will be a starting point — run it through apparmor_parser -p to syntax-check, then test in complain mode and use aa-logprof to refine it from real denials. The model writes the profile skeleton; the complain-mode log refinement is where you learn what the application actually does.
Check yourself
- Distinguish the three rings: what does hardening, an exploit mitigation, and allowlisting each do that the others don't?
- Why deploy an AppArmor profile in complain mode before enforce — what breaks if you skip it?
- What does
aa-statusreporting your critical application as "unconfined" tell you, and why is it a common finding?
Comments
Sign in with GitHub to comment. Choose the type: Feedback (errors or suggestions on this page) · Hints (help for fellow learners — no spoilers) · General (anything else).