You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/linux-hardening/privilege-escalation/docker-security/namespaces/pid-namespace.md
+37-3Lines changed: 37 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -84,11 +84,45 @@ When you enter inside a PID namespace from the default namespace, you will still
84
84
85
85
Also, you can only **enter in another process PID namespace if you are root**. And you **cannot****enter** in other namespace **without a descriptor** pointing to it (like `/proc/self/ns/pid`)
runc ≤1.2.7 allowed attackers that control container images or `runc exec` workloads to replace the container-side `/dev/null` just before the runtime masked sensitive procfs entries. When the race succeeds, `/dev/null` can be turned into a symlink pointing at any host path (for example `/proc/sys/kernel/core_pattern`), so the new container PID namespace suddenly inherits read/write access to host-global procfs knobs even though it never left its own namespace. Once `core_pattern` or `/proc/sysrq-trigger` is writable, generating a coredump or triggering SysRq yields code execution or denial of service in the host PID namespace.
92
+
93
+
Practical workflow:
94
+
95
+
1. Build an OCI bundle whose rootfs replaces `/dev/null` with a link to the host path you want (`ln -sf /proc/sys/kernel/core_pattern rootfs/dev/null`).
96
+
2. Start the container before the fix so runc bind-mounts the host procfs target over the link.
97
+
3. Inside the container namespace, write to the now-exposed procfs file (e.g., point `core_pattern` to a reverse shell helper) and crash any process to force the host kernel to execute your helper as PID 1 context.
98
+
99
+
You can quickly audit whether a bundle is masking the right files before starting it:
100
+
101
+
```bash
102
+
jq '.linux.maskedPaths' config.json | tr -d '"'
103
+
```
104
+
105
+
If the runtime is missing a masking entry you expect (or skips it because `/dev/null` vanished), treat the container as having potential host PID visibility.
106
+
107
+
### Namespace injection with `insject`
92
108
109
+
NCC Group’s `insject` loads as an LD_PRELOAD payload that hooks a late stage in the target program (default `main`) and issues a sequence of `setns()` calls after `execve()`. That lets you attach from the host (or another container) into a victim’s PID namespace *after* its runtime initialized, preserving its `/proc/<pid>` view without having to copy binaries into the container filesystem. Because `insject` can defer joining the PID namespace until it forks, you can keep one thread in the host namespace (with CAP_SYS_PTRACE) while another thread executes in the target PID namespace, creating powerful debugging or offensive primitives.
Key takeaways when abusing or defending against namespace injection:
118
+
119
+
- Use `-S/--strict` to force `insject` to abort if threads already exist or namespace joins fail, otherwise you may leave partly-migrated threads straddling host and container PID spaces.
120
+
- Never attach tools that still hold writable host file descriptors unless you also join the mount namespace—otherwise any process inside the PID namespace can ptrace your helper and reuse those descriptors to tamper with host resources.
-[container escape via "masked path" abuse due to mount race conditions (GitHub Security Advisory)](https://github.com/opencontainers/runc/security/advisories/GHSA-9493-h29p-rfm2)
126
+
-[Tool Release – insject: A Linux Namespace Injector (NCC Group)](https://www.nccgroup.com/us/research-blog/tool-release-insject-a-linux-namespace-injector/)
0 commit comments