Skip to content

Commit bd8abc0

Browse files
authored
Merge pull request #2475 from HackTricks-wiki/research_update_src_linux-hardening_privilege-escalation_container-security_protections_namespaces_cgroup-namespace_20260707_035534
Research Update Enhanced src/linux-hardening/privilege-escal...
2 parents 13d41e2 + dafed16 commit bd8abc0

1 file changed

Lines changed: 59 additions & 6 deletions

File tree

  • src/linux-hardening/privilege-escalation/container-security/protections/namespaces

src/linux-hardening/privilege-escalation/container-security/protections/namespaces/cgroup-namespace.md

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,28 @@ This is mainly a visibility and information-reduction feature. It helps make the
1212

1313
Without a private cgroup namespace, a process may see host-relative cgroup paths that expose more of the machine's hierarchy than is useful. With a private cgroup namespace, `/proc/self/cgroup` and related observations become more localized to the container's own view. This is particularly helpful in modern runtime stacks that want the workload to see a cleaner, less host-revealing environment.
1414

15+
The virtualization also affects `/proc/<pid>/mountinfo`, not only `/proc/<pid>/cgroup`. When you read another process from a different cgroup-namespace perspective, paths outside your namespace root are shown with leading `../` components, which is a handy clue that you are looking above your delegated subtree. A useful nuance for labs and post-exploitation is that a freshly created cgroup namespace often needs a **cgroupfs remount from inside that namespace** before `mountinfo` reflects the new root cleanly. Otherwise you may still see a mount root such as `/..`, which means the inherited mount is still exposing an ancestor-rooted view even though the namespace itself already changed.
16+
1517
## Lab
1618

1719
You can inspect a cgroup namespace with:
1820

1921
```bash
20-
sudo unshare --cgroup --fork bash
22+
sudo unshare --cgroup --mount --fork bash
2123
cat /proc/self/cgroup
24+
cat /proc/self/mountinfo | grep cgroup
2225
ls -l /proc/self/ns/cgroup
2326
```
2427

28+
If you want `mountinfo` to show the new cgroup-namespace root more clearly, remount the cgroup filesystem from inside the new namespace and compare again:
29+
30+
```bash
31+
mount --make-rslave /
32+
umount /sys/fs/cgroup 2>/dev/null
33+
mount -t cgroup2 none /sys/fs/cgroup 2>/dev/null
34+
cat /proc/self/mountinfo | grep cgroup
35+
```
36+
2537
And compare runtime behavior with:
2638

2739
```bash
@@ -35,7 +47,9 @@ The change is mostly about what the process can see, not about whether cgroup en
3547

3648
The cgroup namespace is best understood as a **visibility-hardening layer**. By itself it will not stop a breakout if the container has writable cgroup mounts, broad capabilities, or a dangerous cgroup v1 environment. However, if the host cgroup namespace is shared, the process learns more about how the system is organized and may find it easier to line up host-relative cgroup paths with other observations.
3749

38-
So while this namespace is not usually the star of container breakout writeups, it still contributes to the broader goal of minimizing host information leakage.
50+
On **cgroup v2**, the namespace starts to matter a bit more because delegation rules are tighter. If the hierarchy is mounted with `nsdelegate`, the kernel treats cgroup namespaces as delegation boundaries: ancestor control files are supposed to stay outside the delegatee's reach, and writes at the namespace root are restricted to delegation-safe files such as `cgroup.procs`, `cgroup.threads`, and `cgroup.subtree_control`. This still does not make the namespace an escape primitive by itself, but it changes what a compromised workload can inspect and where it can safely create sub-cgroups.
51+
52+
So while this namespace is not usually the star of container breakout writeups, it still contributes to the broader goal of minimizing host information leakage and constraining cgroup delegation.
3953

4054
## Abuse
4155

@@ -45,6 +59,7 @@ The immediate abuse value is mostly reconnaissance. If the host cgroup namespace
4559
readlink /proc/self/ns/cgroup
4660
cat /proc/self/cgroup
4761
cat /proc/1/cgroup 2>/dev/null
62+
cat /proc/self/mountinfo | grep cgroup
4863
```
4964

5065
If writable cgroup paths are also exposed, combine that visibility with a search for dangerous legacy interfaces:
@@ -56,6 +71,34 @@ find /sys/fs/cgroup -maxdepth 3 -writable 2>/dev/null | head -n 50
5671

5772
The namespace itself rarely gives instant escape, but it often makes the environment easier to map before testing cgroup-based abuse primitives.
5873

74+
A quick runtime reality check also helps prioritize the attack path. Docker exposes `--cgroupns=host|private`, while Podman supports `host`, `private`, `container:<id>`, and `ns:<path>`. On Podman specifically, the default is usually **`host` on cgroup v1** and **`private` on cgroup v2**, so simply identifying the cgroup version already tells you which namespace posture is more likely before you even inspect the full OCI config.
75+
76+
### Modern v2 Recon: Is This A Delegated Subtree?
77+
78+
On modern hosts the interesting question is often not `release_agent`, but whether the current process is sitting inside a delegated **cgroup v2** subtree with enough visibility or write access to build nested groups:
79+
80+
```bash
81+
stat -fc %T /sys/fs/cgroup
82+
cat /sys/fs/cgroup/cgroup.controllers 2>/dev/null
83+
cat /sys/fs/cgroup/cgroup.subtree_control 2>/dev/null
84+
cat /sys/fs/cgroup/cgroup.events 2>/dev/null
85+
```
86+
87+
Useful interpretation:
88+
89+
- `cgroup2fs` means you are in the unified v2 hierarchy, so classic v1-only `release_agent` chains should stop being your first guess.
90+
- `cgroup.controllers` shows which controllers are available from the parent and therefore what the current subtree could potentially fan out to children.
91+
- `cgroup.subtree_control` shows which controllers are actually enabled for descendants.
92+
- `cgroup.events` exposes `populated=0/1`, which is handy for watching whether a subtree has become empty, but it is **not** a host-code-execution primitive like v1 `release_agent`.
93+
94+
If you already have enough privilege to inspect another process namespace directly, compare views with:
95+
96+
```bash
97+
nsenter -t <pid> -C -- bash
98+
readlink /proc/self/ns/cgroup
99+
cat /proc/self/cgroup
100+
```
101+
59102
### Full Example: Shared cgroup Namespace + Writable cgroup v1
60103

61104
The cgroup namespace alone is usually not enough for escape. The practical escalation happens when host-revealing cgroup paths are combined with writable cgroup v1 interfaces:
@@ -75,16 +118,26 @@ Without writable cgroup interfaces, the impact is usually limited to reconnaissa
75118
The point of these commands is to see whether the process has a private cgroup namespace view or is learning more about the host hierarchy than it really needs.
76119

77120
```bash
78-
readlink /proc/self/ns/cgroup # Namespace identifier for cgroup view
79-
cat /proc/self/cgroup # Visible cgroup paths from inside the workload
80-
mount | grep cgroup # Mounted cgroup filesystems and their type
121+
readlink /proc/self/ns/cgroup # Namespace identifier for cgroup view
122+
cat /proc/self/cgroup # Visible cgroup paths from inside the workload
123+
cat /proc/self/mountinfo | grep cgroup
124+
stat -fc %T /sys/fs/cgroup # cgroup2fs -> v2 unified hierarchy
125+
cat /sys/fs/cgroup/cgroup.controllers 2>/dev/null
126+
mount | grep cgroup
81127
```
82128

83129
What is interesting here:
84130

85131
- If the namespace identifier matches a host process you care about, the cgroup namespace may be shared.
86-
- Host-revealing paths in `/proc/self/cgroup` are useful reconnaissance even when they are not directly exploitable.
132+
- Host-revealing paths in `/proc/self/cgroup` or ancestor-rooted entries in `mountinfo` are useful reconnaissance even when they are not directly exploitable.
133+
- If `cgroup2fs` is in use, focus on delegation, visible controllers, and writable subtrees rather than assuming old v1 primitives still exist.
87134
- If cgroup mounts are also writable, the visibility question becomes much more important.
88135

89136
The cgroup namespace should be treated as a visibility-hardening layer rather than as a primary escape-prevention mechanism. Exposing host cgroup structure unnecessarily adds reconnaissance value for the attacker.
137+
138+
## References
139+
140+
- [Linux cgroup_namespaces(7)](https://man7.org/linux/man-pages/man7/cgroup_namespaces.7.html)
141+
- [Linux kernel cgroup v2 documentation](https://docs.kernel.org/admin-guide/cgroup-v2.html)
142+
90143
{{#include ../../../../../banners/hacktricks-training.md}}

0 commit comments

Comments
 (0)