Executive summary
A sandbox escape is the step that turns a local code-execution bug into a full-system compromise. In a typical chain—renderer RCE → sandbox escape → privilege escalation—the escape pivots from a restricted security boundary (browser renderer, PDF viewer, mobile app, container, VM guest, or serverless function) into a more privileged broker or the host OS. This article gives you a practitioner’s view: how escapes are built, where they commonly fail, and how to prevent, detect, and respond to them.
1) What “sandbox” means in practice
A sandbox enforces a policy boundary between untrusted code and sensitive resources:
- Browser/Document: Renderer processes jailed by Job objects/AppContainer (Windows), seatbelt/sandboxd (macOS), namespaces+seccomp (Linux).
- Mobile: iOS entitlements & sandboxd, Android SELinux + app UID.
- Containers: Linux namespaces (user, mount, pid, net, uts), cgroups, capabilities, seccomp-BPF.
- Serverless/VM: micro-VMs (e.g., Firecracker), gVisor, Kata; still rely on kernel/host interfaces.
- PDF/Office sandboxes: separate worker processes with restricted file/registry access.
Security invariant: untrusted code may ask, but the broker or kernel decides.
2) Attacker’s playbook: common escape techniques
A. Broker/IPC abuse
Most sandboxes proxy sensitive operations via a broker (ALPC/COM on Windows, XPC on macOS, sockets/DBus on Linux). Escapes arise when:
- Policy confusion: broker authorizes based on caller-provided paths without canonicalization.
- TOCTOU (Time-of-check to time-of-use): attacker swaps symlinks between validation and open.
- Handle confusion/duplication: broker returns a handle with broader rights; attacker reuses/duplicates it.
- Message confusion: untrusted fields are trusted (integer overflow, size miscalc) → memory corruption.
B. Filesystem tricks
- Symlink races & directory traversal (
..\..\,%2e%2e%2f) to escape allowed directories. - Hardlink attacks to smuggle privileged writes.
- Mount namespace quirks (containers): bind-mounts exposing
/proc,/sys, or host sockets.
C. Kernel vulnerabilities
Sandbox policies ultimately terminate in syscalls; a kernel UAF/OOB/race/ioctl mishandling can jump straight to ring-0.
- Typical flow: trigger bug → arbitrary read/write → disable SMEP/SMAP/CFG → escalate creds.
D. Logic flaws & mis-scoped capabilities (containers)
- Over-privileged Linux capabilities (e.g.,
CAP_SYS_ADMIN,CAP_SYS_MODULE), or user namespaces paired with vulnerable filesystems (overlayfs) → host file write. - Docker/Podman run options that punch holes (
--privileged,--pid=host,-v /:/host).
E. JIT/type confusion (browser renderers)
- JS engine bug → renderer RCE; escape via GPU/IPC bugs, vulnerable system service (Win32k historically), or permissive broker call.
F. Serverless/VM edges
- Hypercall/virtio bugs, shared disk snapshot races, or side channels leaking host data (page cache, eBPF maps).
3) How real exploits are built (the chain mindset)
- Primitive acquisition
- Info leak (bypass ASLR), addrof/fakeobj (JS), or kernel arbitrary read/write.
- Policy bypass
- Send crafted IPC to broker; exploit path validation or race; coerce broker to open outside the sandbox root.
- Privilege pivot
- Duplicate token/port/handle with higher rights; spawn broker-child with medium/high integrity (Windows) or escape to host namespace (containers).
- Stabilization
- Install persistence (scheduled task/launchd unit/cron), dump credentials/tokens, disable sensors.
4) Defensive engineering: break the chain
4.1 Broker & IPC hardening
- Canonicalize before authorize. On Linux use
openat2()withRESOLVE_BENEATH|RESOLVE_NO_SYMLINKSto defeat traversal & symlink races:
cCopyEditstruct open_how how = {
.flags = O_RDONLY,
.resolve = RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS
};
int fd = openat2(dirfd, user_path, &how, sizeof(how)); // returns -EXDEV on escape attempts
- Design for idempotence: validate and use the same descriptor (avoid path re-resolution).
- Explicit allow-lists of verbs; no “raw file open” passthrough.
- Broker personality: drop privileges, job object limits,
NO_SYMBOLIC_LINKS(Windows),O_NOFOLLOW.
4.2 Linux container policy
- Drop capabilities aggressively (keep minimal e.g.,
CAP_NET_BIND_SERVICEonly if needed). - Seccomp-BPF: deny dangerous syscalls (
keyctl,ptrace,bpf,mount,kexec_load,clone3with flags). - Read-only root +
no_new_privs+ masked/proc/sys,/sys. - Userns + rootless if possible; avoid host PID/NET namespaces.
Docker example (compose)
yamlCopyEditservices:
app:
image: app:latest
read_only: true
security_opt:
- "no-new-privileges:true"
cap_drop: [ALL]
cap_add: ["NET_BIND_SERVICE"]
pids_limit: 256
tmpfs: ["/tmp:rw,noexec,nosuid,nodev"]
4.3 Windows AppContainer best practices
- Use AppContainer with Capabilities instead of legacy MIC.
- Mark broker binaries with
/guard:cf(Control Flow Guard), enable CET, and low-privilege service SIDs. - Use ALPC with SDDL access filters; avoid global named objects.
4.4 macOS/iOS
- Minimize entitlements; gate XPC by audit token (process identity), not bundle ID strings.
- Enable Hardened Runtime, sandbox profiles (
com.apple.security.app-sandbox), and System Integrity Protection aware paths.
4.5 Memory-safety & compiler defenses
- Prefer memory-safe languages for brokers/tooling.
- Enable ASLR, DEP/W^X, CET, PAC; on Linux enable
-fstack-protector-strong, CFI (Clang), Fortify.
5) Detection & response: what to log and alert on
Telemetry to collect
- Process tree: sandboxed renderer → broker → unexpected child (shell, script).
- IPC anomalies: broker crashes, malformed message counters, denied verbs.
- Filesystem: repeated
../attempts, rapid symlink creation,openatfailures withEXDEV/EPERM. - Kernel: KASLR leaks, unusual eBPF/ptrace attempts, seccomp violations.
Ready-to-use rules
Sigma – Web/renderer spawning shell
yamlCopyEdittitle: Sandbox/Broker Spawns Shell
logsource: windows
detection:
sel:
ParentImage|endswith:
- '\chrome.exe'
- '\firefox.exe'
- '\AcroRd32.exe'
- '\w3wp.exe'
Image|endswith:
- '\cmd.exe'
- '\powershell.exe'
- '\bash.exe'
condition: sel
level: high
Linux auditd – symlink race attempts
bashCopyEdit-w /tmp -p wa -k tmpwatch
-a always,exit -F arch=b64 -S symlink,link,rename,linkat -k link_ops
KQL – Defender for Endpoint (broker misuse)
kustoCopyEditDeviceProcessEvents
| where InitiatingProcessFileName in ("chrome.exe","acrobat.exe","plugin-container.exe")
| where FileName in ("cmd.exe","powershell.exe","bash","sh")
| summarize cnt=count() by DeviceName, InitiatingProcessFileName, FileName, bin(Timestamp, 5m)
Incident playbook (suspected escape)
- Contain: isolate endpoint/container/VM; snapshot memory/disk.
- Triage: pull broker crash dumps, IPC logs, seccomp violations, renderer minidumps.
- Scope: search enterprise for identical process trees, indicators (named pipe/ALPC path, temp file names).
- Recover: rebuild brokers from golden images; rotate tokens/SSO sessions; add virtual patches (WAF/seccomp rule).
- Lessons learned: add regression tests (symlink races, path canonicalization, fuzzing harness).
6) Fuzzing & verification you should run
- AFL++/libFuzzer/honggfuzz against broker parsers and IPC message decoders.
- syzkaller for kernel IOCTLs exposed to sandboxed processes.
- Race testers: chaos monkey to perform parallel
link/renameduring broker opens. - Property tests: path invariants (never escape root), handle rights (no write/execute).
7) Red-team checklist (for control validation)
- Can the renderer/broker write outside its depot?
- Do
../and URL-encoded traversal bypass guards? - Are named objects (pipes, mutexes, shared memory) correctly ACL’d?
- Does the container run with any extra capabilities?
- Are seccomp and read-only root actually active?
- Can a sandboxed app cause the broker to open
/etc/shadoworC:\Windows\System32\config\SAMindirectly?
8) 30–60–90 day program plan
Days 1–30 – Baseline & hardening
- Inventory all sandboxes/brokers; enable full telemetry.
- Apply container baseline (cap drop, seccomp, read-only FS, tmpfs).
- Threat model each IPC verb; add canonicalization and
openat2where applicable.
Days 31–60 – Testing & detections
- Add fuzzers to CI; seed with real IPC traffic.
- Deploy Sigma/auditd/eBPF rules; hunt for existing exploit patterns.
- Introduce canary verbs and honeyfiles to detect misuse.
Days 61–90 – Resilience & governance
- Memory-safe rewrites for high-risk broker surfaces.
- Quarterly red-team focusing on escape-to-host (MITRE ATT&CK T1611) and Exploitation for Privilege Escalation (T1068).
- Codify break-glass procedures & circuit-breakers for auto-containment.
Takeaways
- Sandboxes fail at boundaries: IPC, file paths, capabilities, and kernel edges.
- Escape prevention is mostly policy engineering (allow-lists, canonicalization, least privilege) backed by kernel mitigations and memory safety.
- Treat brokers and container runtimes as Tier-0 software, with fuzzing, SBOMs, and secure release engineering.
- Logging the right signals (process trees, IPC errors, seccomp denials) is the difference between knowing and guessing.
Leave a comment