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

time-namespace.md (10273B)


      1 ---
      2 title: "Time Namespace"
      3 section: "Linux"
      4 sectionSlug: "linux-hardening"
      5 sourcePath: "src/linux-hardening/containers-namespaces/container-security/protections/namespaces/time-namespace.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/containers-namespaces/container-security/protections/namespaces/time-namespace.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Time Namespace
     14 
     15 ## Overview
     16 
     17 The time namespace virtualizes selected monotonic-style clocks instead of the host wall clock. In practice this means private offsets for **`CLOCK_MONOTONIC`** and **`CLOCK_BOOTTIME`**, plus the closely related **`CLOCK_MONOTONIC_COARSE`**, **`CLOCK_MONOTONIC_RAW`**, and **`CLOCK_BOOTTIME_ALARM`** views. It does **not** virtualize **`CLOCK_REALTIME`**, so `date` and certificate-expiry logic still observe the host wall clock unless some other mechanism interferes.<sup>[[1]](#references)</sup>
     18 
     19 The main purpose is to let a process observe controlled elapsed-time offsets without changing the host's global time view. This is useful for checkpoint/restore workflows, deterministic testing, and advanced runtime behavior. It is not usually a headline isolation control in the same way as mount or user namespaces, but it still contributes to making the process environment more self-contained.
     20 
     21 From an offensive point of view, this namespace is usually more relevant for **reconnaissance, timer skew, and runtime understanding** than for a direct breakout. Still, it matters because more container runtimes and checkpoint/restore workflows are now able to request it explicitly.
     22 
     23 ## Lab
     24 
     25 If the host kernel and userspace support it, you can inspect the namespace with:
     26 
     27 ```bash
     28 sudo unshare --time --fork bash
     29 ls -l /proc/self/ns/time /proc/self/ns/time_for_children
     30 python3 - <<'PY'
     31 import time
     32 print("realtime :", time.time())
     33 print("monotonic:", time.clock_gettime(time.CLOCK_MONOTONIC))
     34 print("boottime :", time.clock_gettime(time.CLOCK_BOOTTIME))
     35 PY
     36 cat /proc/uptime
     37 date
     38 ```
     39 
     40 Support varies by kernel and tool versions, so this page is more about understanding the mechanism than expecting it to be visible in every lab environment. The important observation is that `date` should still reflect the host wall clock, while monotonic/boottime-based values are the ones that change when nonzero offsets are configured.
     41 
     42 ### Creation Nuance
     43 
     44 Time namespaces are slightly unusual compared to mount, PID, or network namespaces:<sup>[[1]](#references)</sup>
     45 
     46 - `unshare(CLONE_NEWTIME)` creates a new time namespace for **future children**.
     47 - The calling task stays in its current time namespace.
     48 - `/proc/<pid>/ns/time_for_children` is therefore often more interesting than `/proc/<pid>/ns/time` when debugging runtime setup.
     49 
     50 The write window is also special. Offsets in `/proc/<pid>/timens_offsets` must be written before the new time namespace is fully populated with running tasks; in practice runtimes do this during the narrow setup window between namespace creation and starting the final payload. Once a task is already running there, later writes fail with `EACCES`. This is why low-level runtimes handle time-namespace setup as an early bootstrap step instead of trying to patch offsets from inside an already-started container process.<sup>[[1]](#references)</sup>
     51 
     52 ### Time Offsets
     53 
     54 Linux time namespaces expose the per-namespace offsets through `/proc/<pid>/timens_offsets`. The format is a set of clock names or IDs plus second/nanosecond deltas relative to the initial time namespace.<sup>[[1]](#references)</sup>
     55 
     56 In practice, the most reliable user-facing workflow is to let `unshare` write those offsets for you:
     57 
     58 ```bash
     59 sudo unshare -UrT --fork --mount-proc --monotonic 86400 --boottime 604800 bash
     60 cat /proc/$$/timens_offsets 2>/dev/null
     61 python3 - <<'PY'
     62 import time
     63 print("monotonic:", time.clock_gettime(time.CLOCK_MONOTONIC))
     64 print("boottime :", time.clock_gettime(time.CLOCK_BOOTTIME))
     65 print("uptime   :", open("/proc/uptime").read().split()[0])
     66 PY
     67 ```
     68 
     69 The important point is not the exact command syntax but the behavior: a container can observe a different uptime-like view without changing the host wall clock.
     70 
     71 ### `unshare` Helper Flags
     72 
     73 Recent `util-linux` versions provide convenience flags that write the offsets automatically during namespace creation:
     74 
     75 ```bash
     76 sudo unshare -T --fork --monotonic 86400 --boottime 604800 --mount-proc bash
     77 ```
     78 
     79 These flags are mostly a usability improvement, but they also make it easier to recognize the feature in documentation, test harnesses, and runtime wrappers.
     80 
     81 ## Runtime Usage
     82 
     83 Time namespaces are newer and less universally exercised than mount or PID namespaces. OCI Runtime Specification v1.1 added explicit support for the `time` namespace and the `linux.timeOffsets` field, and modern runtimes can map that data into the kernel bootstrap flow. A minimal OCI fragment looks like:
     84 
     85 ```json
     86 {
     87   "linux": {
     88     "namespaces": [
     89       { "type": "time" }
     90     ],
     91     "timeOffsets": {
     92       "monotonic": 86400,
     93       "boottime": 600
     94     }
     95   }
     96 }
     97 ```
     98 
     99 This matters because it turns time namespacing from a niche kernel primitive into something that runtimes can request portably. It also explains why runtime internals need an explicit synchronization step: the offset must be written to `/proc/<pid>/timens_offsets` before the container payload fully enters the new namespace.
    100 
    101 Checkpoint/restore stacks such as CRIU are one of the main real-world reasons this exists at all. Without time namespaces, restoring a paused workload would make monotonic and boot-time clocks jump by the amount of time the workload spent suspended.<sup>[[2]](#references)</sup>
    102 
    103 ## Security Impact
    104 
    105 There are fewer classic breakout stories centered on the time namespace than on other namespace types. The risk here is usually not that the time namespace directly enables escape, but that readers ignore it completely and therefore miss how advanced runtimes may be shaping process behavior.
    106 
    107 In specialized environments, altered monotonic or boottime views can affect:
    108 
    109 - timeout and retry behavior
    110 - watchdogs and lease logic
    111 - `timerfd`, `nanosleep`, and `clock_nanosleep` behavior
    112 - checkpoint/restore forensics
    113 - elapsed-time telemetry and uptime-based heuristics
    114 
    115 So while this is rarely the first namespace you abuse, it can absolutely explain "impossible" timing behavior during an assessment.
    116 
    117 ## Abuse
    118 
    119 There is usually no direct breakout primitive here, but altered clock behavior can still be useful for understanding the execution environment, identifying advanced runtime features, and spotting timer-based logic that is measured against monotonic clocks instead of wall clock time:
    120 
    121 ```bash
    122 readlink /proc/self/ns/time
    123 readlink /proc/self/ns/time_for_children
    124 cat /proc/$$/timens_offsets 2>/dev/null
    125 python3 - <<'PY'
    126 import time
    127 print("realtime :", time.time())
    128 print("monotonic:", time.clock_gettime(time.CLOCK_MONOTONIC))
    129 print("boottime :", time.clock_gettime(time.CLOCK_BOOTTIME))
    130 print("uptime   :", open("/proc/uptime").read().split()[0])
    131 PY
    132 ```
    133 
    134 If you are comparing two processes, differences here can help explain odd timing behavior, checkpoint/restore artifacts, or environment-specific logging mismatches.
    135 
    136 Practical attacker-relevant angles:
    137 
    138 - confuse backoff, sleep, or watchdog logic implemented with monotonic clocks
    139 - explain why `/proc/uptime` and timer-driven behavior disagree with host-side wall-clock expectations
    140 - recognize CRIU/checkpoint-restore workflows and other advanced runtime features
    141 - spot environments where joining a target time namespace with `nsenter -T -t <pid> -- ...` may reproduce container-local timer behavior for debugging or post-exploitation
    142 
    143 Impact:
    144 
    145 - almost always reconnaissance or environment understanding
    146 - useful for explaining logging, uptime, or checkpoint/restore anomalies
    147 - useful for analyzing monotonic-time-based sleeps, retries, and timers
    148 - not normally a direct container-escape mechanism by itself
    149 
    150 The important abuse nuance is that time namespaces do not virtualize `CLOCK_REALTIME`, so they do not by themselves let an attacker falsify the host wall clock or directly break certificate-expiry checks system-wide. Their value is mostly in confusing monotonic-time-based logic, reproducing environment-specific bugs, or understanding advanced runtime behavior.
    151 
    152 ## Checks
    153 
    154 These checks are mostly about confirming whether the runtime is using a private time namespace at all and whether it actually set nonzero offsets.
    155 
    156 ```bash
    157 readlink /proc/self/ns/time                 # Current time namespace identifier
    158 readlink /proc/self/ns/time_for_children    # Time namespace inherited by children
    159 cat /proc/$$/timens_offsets 2>/dev/null     # Monotonic and boottime offsets when supported
    160 lsns -t time 2>/dev/null                    # Host-side inventory when available
    161 python3 - <<'PY'
    162 import time
    163 print("realtime :", time.time())
    164 print("monotonic:", time.clock_gettime(time.CLOCK_MONOTONIC))
    165 print("boottime :", time.clock_gettime(time.CLOCK_BOOTTIME))
    166 PY
    167 ```
    168 
    169 What is interesting here:
    170 
    171 - In many environments these values will not lead to an immediate security finding, but they do tell you whether a specialized runtime feature is in play.
    172 - If `time_for_children` differs from `time`, the caller may have prepared a child-only time namespace that it has not entered itself.
    173 - If `date` matches the host but monotonic/boottime-based values do not, you are probably looking at time namespacing rather than wall-clock tampering.
    174 - If you are comparing two processes, differences here may explain confusing timing or checkpoint/restore behavior.
    175 
    176 For most container breakouts, the time namespace is not the first control you will investigate. Still, a complete container-security section should mention it because it is part of the modern kernel model and occasionally matters in advanced runtime scenarios.
    177 
    178 ## References
    179 
    180 - [1] [Linux `time_namespaces(7)` manual page](https://man7.org/linux/man-pages/man7/time_namespaces.7.html)
    181 - [2] [Time Namespaces: Per-Container Clock Offsets for CLOCK_MONOTONIC / CLOCK_BOOTTIME - Linux Kernel Internals](https://kernel-internals.org/time/time-namespaces/)