daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

cgroup-namespace.md (9075B)


      1 ---
      2 title: "cgroup Namespace"
      3 section: "Linux"
      4 sectionSlug: "linux-hardening"
      5 sourcePath: "src/linux-hardening/containers-namespaces/container-security/protections/namespaces/cgroup-namespace.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/containers-namespaces/container-security/protections/namespaces/cgroup-namespace.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # cgroup Namespace
     14 
     15 ## Overview
     16 
     17 The cgroup namespace does not replace cgroups and does not itself enforce resource limits. Instead, it changes **how the cgroup hierarchy appears** to the process. In other words, it virtualizes the visible cgroup path information so that the workload sees a container-scoped view rather than the full host hierarchy.
     18 
     19 This is mainly a visibility and information-reduction feature. It helps make the environment look self-contained and reveals less about the host's cgroup layout. That may sound modest, but it still matters because unnecessary visibility into host structure can aid reconnaissance and simplify environment-dependent exploit chains.
     20 
     21 ## Operation
     22 
     23 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.
     24 
     25 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.<sup>[[1]](#references)</sup>
     26 
     27 ## Lab
     28 
     29 You can inspect a cgroup namespace with:
     30 
     31 ```bash
     32 sudo unshare --cgroup --mount --fork bash
     33 cat /proc/self/cgroup
     34 cat /proc/self/mountinfo | grep cgroup
     35 ls -l /proc/self/ns/cgroup
     36 ```
     37 
     38 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:
     39 
     40 ```bash
     41 mount --make-rslave /
     42 umount /sys/fs/cgroup 2>/dev/null
     43 mount -t cgroup2 none /sys/fs/cgroup 2>/dev/null
     44 cat /proc/self/mountinfo | grep cgroup
     45 ```
     46 
     47 And compare runtime behavior with:
     48 
     49 ```bash
     50 docker run --rm debian:stable-slim cat /proc/self/cgroup
     51 docker run --rm --cgroupns=host debian:stable-slim cat /proc/self/cgroup
     52 ```
     53 
     54 The change is mostly about what the process can see, not about whether cgroup enforcement exists.
     55 
     56 ## Security Impact
     57 
     58 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.
     59 
     60 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`.<sup>[[2]](#references)</sup> 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.
     61 
     62 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.
     63 
     64 ## Abuse
     65 
     66 The immediate abuse value is mostly reconnaissance. If the host cgroup namespace is shared, compare the visible paths and look for host-revealing hierarchy details:
     67 
     68 ```bash
     69 readlink /proc/self/ns/cgroup
     70 cat /proc/self/cgroup
     71 cat /proc/1/cgroup 2>/dev/null
     72 cat /proc/self/mountinfo | grep cgroup
     73 ```
     74 
     75 If writable cgroup paths are also exposed, combine that visibility with a search for dangerous legacy interfaces:
     76 
     77 ```bash
     78 find /sys/fs/cgroup -maxdepth 3 -name release_agent 2>/dev/null -exec ls -l {} \;
     79 find /sys/fs/cgroup -maxdepth 3 -writable 2>/dev/null | head -n 50
     80 ```
     81 
     82 The namespace itself rarely gives instant escape, but it often makes the environment easier to map before testing cgroup-based abuse primitives.
     83 
     84 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.
     85 
     86 ### Modern v2 Recon: Is This A Delegated Subtree?
     87 
     88 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:
     89 
     90 ```bash
     91 stat -fc %T /sys/fs/cgroup
     92 cat /sys/fs/cgroup/cgroup.controllers 2>/dev/null
     93 cat /sys/fs/cgroup/cgroup.subtree_control 2>/dev/null
     94 cat /sys/fs/cgroup/cgroup.events 2>/dev/null
     95 ```
     96 
     97 Useful interpretation:
     98 
     99 - `cgroup2fs` means you are in the unified v2 hierarchy, so classic v1-only `release_agent` chains should stop being your first guess.
    100 - `cgroup.controllers` shows which controllers are available from the parent and therefore what the current subtree could potentially fan out to children.
    101 - `cgroup.subtree_control` shows which controllers are actually enabled for descendants.
    102 - `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`.
    103 
    104 If you already have enough privilege to inspect another process namespace directly, compare views with:
    105 
    106 ```bash
    107 nsenter -t <pid> -C -- bash
    108 readlink /proc/self/ns/cgroup
    109 cat /proc/self/cgroup
    110 ```
    111 
    112 ### Full Example: Shared cgroup Namespace + Writable cgroup v1
    113 
    114 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:
    115 
    116 ```bash
    117 cat /proc/self/cgroup
    118 find /sys/fs/cgroup -maxdepth 3 -name release_agent 2>/dev/null
    119 find /sys/fs/cgroup -maxdepth 3 -name notify_on_release 2>/dev/null | head
    120 ```
    121 
    122 If those files are reachable and writable, pivot immediately into the full `release_agent` exploitation flow from [cgroups.md](/hacktricks/linux-hardening/containers-namespaces/container-security/protections/cgroups). The impact is host code execution from inside the container.
    123 
    124 Without writable cgroup interfaces, the impact is usually limited to reconnaissance.
    125 
    126 ## Checks
    127 
    128 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.
    129 
    130 ```bash
    131 readlink /proc/self/ns/cgroup       # Namespace identifier for cgroup view
    132 cat /proc/self/cgroup               # Visible cgroup paths from inside the workload
    133 cat /proc/self/mountinfo | grep cgroup
    134 stat -fc %T /sys/fs/cgroup          # cgroup2fs -> v2 unified hierarchy
    135 cat /sys/fs/cgroup/cgroup.controllers 2>/dev/null
    136 mount | grep cgroup
    137 ```
    138 
    139 What is interesting here:
    140 
    141 - If the namespace identifier matches a host process you care about, the cgroup namespace may be shared.
    142 - Host-revealing paths in `/proc/self/cgroup` or ancestor-rooted entries in `mountinfo` are useful reconnaissance even when they are not directly exploitable.
    143 - If `cgroup2fs` is in use, focus on delegation, visible controllers, and writable subtrees rather than assuming old v1 primitives still exist.
    144 - If cgroup mounts are also writable, the visibility question becomes much more important.
    145 
    146 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.
    147 
    148 ## References
    149 
    150 - [1] [cgroup_namespaces(7) — Linux manual page](https://man7.org/linux/man-pages/man7/cgroup_namespaces.7.html)
    151 - [2] [Control Group v2 — The Linux Kernel documentation](https://docs.kernel.org/admin-guide/cgroup-v2.html)