Running arbitrary ELFs on noexec,nosuid,nodev mounts – without ever whispering execve(2) to the kernel.
A straight-from-the-underground deep dive into the userland-exec toolkit. We built it. We drop it. We own the box.
The Radical Threat Model (because “defense in depth” is just marketing until you test it)
Sysadmins think they’ve won when they slap this on:
mount -t tmpfs -o noexec,nosuid,nodev tmpfs /tmp/noexec_demo
Add SELinux enforcing or a tight AppArmor profile and they call it a day.
But the real threat model – the one the underground has respected since the grugq days – is brutally simple:
Attacker launches exploit | v +-------------------------+ | Remote code execution | | (stack smash, fmtstr, | | use-after-free...) | +-------------------------+ | v Target: Hardened Linux Box ┌─────────────────────────────────────┐ │ • SELinux (enforcing) or AppArmor │ │ • /tmp mounted noexec,nosuid,nodev │ │ • Full modern mitigations │ └─────────────────────────────────────┘ | v Process pwned -- arbitrary code exec | v USERLAND-EXEC TAKES OVER ┌─────────────────────────────────────┐ │ • Parse ELF in userspace │ │ • mmap / memfd_create segments │ │ • W^X signal handler dance │ │ • Direct jmp to e_entry │ └─────────────────────────────────────┘ | v Stealthy RCE achieved. Original process name & context intact. No execve(2) ever called. No MAC hooks. noexec? What noexec.
May your userland-exec payload not nuke your own cyber bunker.
This is exactly the scenario laid out in the grsecurity-101 threat model documentation: once arbitrary code execution is achieved inside a process, filesystem-based and execve-centric controls become largely theater. The kernel never gets a chance to enforce noexec because the binary never travels through the kernel’s execution path.
... continue reading