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

euid-ruid-suid.md (9842B)


      1 ---
      2 title: "euid, ruid, suid"
      3 section: "Linux"
      4 sectionSlug: "linux-hardening"
      5 sourcePath: "src/linux-hardening/user-information/euid-ruid-suid.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/user-information/euid-ruid-suid.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # euid, ruid, suid
     14 
     15 ### User Identification Variables
     16 
     17 - **`ruid`**: The **real user ID** denotes the user who initiated the process.<sup>[[1]](#references)</sup>
     18 - **`euid`**: Known as the **effective user ID**, it represents the user identity utilized by the system to ascertain process privileges. Generally, `euid` mirrors `ruid`, barring instances like a SetUID binary execution (when the set-user-ID transition is honored), where `euid` assumes the file owner's identity, thus granting specific operational permissions.<sup>[[1]](#references)[[5]](#references)</sup>
     19 - **`suid`**: This **saved user ID** is pivotal when a high-privilege process (typically running as root) needs to temporarily relinquish its privileges to perform certain tasks, only to later reclaim its initial elevated status.<sup>[[1]](#references)</sup>
     20 
     21 #### Important Note
     22 
     23 An unprivileged process can only modify its `euid` to match the current `ruid`, `euid`, or `suid`.<sup>[[3]](#references)</sup>
     24 
     25 ### Understanding set\*uid Functions
     26 
     27 - **`setuid`**: Contrary to initial assumptions, `setuid` sets the calling process's `euid`. For a privileged process, it also sets `ruid` and `suid` to the specified user; after all IDs are set to root, the process cannot regain a previous identity using `setuid`. Detailed insights can be found in the [setuid man page](https://man7.org/linux/man-pages/man2/setuid.2.html).<sup>[[2]](#references)</sup>
     28 - **`setreuid`** and **`setresuid`**: `setreuid` changes `ruid` and `euid`, while `setresuid` changes all three IDs. For an unprivileged process, `setresuid` restricts each target to the current `ruid`, `euid`, or `suid`; `setreuid` restricts `euid` to those values and `ruid` to the current `ruid` or `euid`. A process with `CAP_SETUID` can assign arbitrary values to the IDs supported by each call. More information can be gleaned from the [setresuid man page](https://man7.org/linux/man-pages/man2/setresuid.2.html) and the [setreuid man page](https://man7.org/linux/man-pages/man2/setreuid.2.html).<sup>[[3]](#references)[[4]](#references)</sup>
     29 
     30 These functionalities are designed not as a security mechanism but to facilitate the intended operational flow, such as when a program adopts another user's identity by altering its effective user ID.<sup>[[1]](#references)[[3]](#references)[[4]](#references)</sup>
     31 
     32 Notably, a privileged call to `setuid` can assign all three IDs, whereas `setreuid` and `setresuid` expose different controls; differentiating these functions is crucial for understanding user-ID transitions.<sup>[[1]](#references)[[2]](#references)[[3]](#references)[[4]](#references)</sup>
     33 
     34 ### Program Execution Mechanisms in Linux
     35 
     36 #### **`execve` System Call**
     37 
     38 - **Functionality**: `execve` initiates a program, determined by the first argument. It takes two array arguments, `argv` for arguments and `envp` for the environment.<sup>[[5]](#references)</sup>
     39 - **Behavior**: It retains the memory space of the caller but refreshes the stack, heap, and data segments. The program's code is replaced by the new program.<sup>[[5]](#references)</sup>
     40 - **User ID Preservation**:
     41   - `ruid` and supplementary group IDs remain unaltered.<sup>[[5]](#references)</sup>
     42   - `euid` is normally unchanged but might change if the new program has the SetUID bit set.<sup>[[5]](#references)</sup>
     43   - `suid` gets updated from `euid` post-execution.<sup>[[5]](#references)</sup>
     44 - **Documentation**: Detailed information can be found on the [`execve` man page](https://man7.org/linux/man-pages/man2/execve.2.html).<sup>[[5]](#references)</sup>
     45 
     46 #### **`system` Function**
     47 
     48 - **Functionality**: Unlike `execve`, `system` behaves as if it creates a child process using `fork` and executes the command within that child process using `execl`.<sup>[[6]](#references)</sup>
     49 - **Command Execution**: Executes the command via `sh` with `execl("/bin/sh", "sh", "-c", command, (char *) NULL);`.<sup>[[6]](#references)</sup>
     50 - **Behavior**: As `execl` is an `exec`-family call, it operates similarly to `execve` but in the context of a new child process.<sup>[[1]](#references)[[5]](#references)[[6]](#references)</sup>
     51 - **Documentation**: Further insights can be obtained from the [`system` man page](https://man7.org/linux/man-pages/man3/system.3.html).<sup>[[6]](#references)</sup>
     52 
     53 #### **Behavior of `bash` and `sh` with SUID**
     54 
     55 - **`bash`**:
     56   - Has a `-p` option influencing how `euid` and `ruid` are treated.<sup>[[7]](#references)</sup>
     57   - Without `-p`, `bash` sets `euid` to `ruid` if they initially differ.<sup>[[7]](#references)</sup>
     58   - With `-p`, the initial `euid` is preserved.<sup>[[7]](#references)</sup>
     59   - More details can be found on the [`bash` man page](https://linux.die.net/man/1/bash).<sup>[[7]](#references)</sup>
     60 - **`sh`**:
     61   - POSIX `sh` does not define a Bash-style `-p` privilege-preservation option.<sup>[[8]](#references)</sup>
     62   - Its POSIX option list includes `-i`, which selects interactive mode and may be rejected when the real and effective IDs differ.<sup>[[8]](#references)</sup>
     63   - Additional information is available on the [`sh` man page](https://man7.org/linux/man-pages/man1/sh.1p.html).<sup>[[8]](#references)</sup>
     64 
     65 These mechanisms, distinct in their operation, offer a versatile range of options for executing and transitioning between programs, with specific nuances in how user IDs are managed and preserved.
     66 
     67 ### Testing User ID Behaviors in Executions
     68 
     69 Examples taken from https://0xdf.gitlab.io/2022/05/31/setuid-rabbithole.html#testing-on-jail, check it for further information.<sup>[[1]](#references)</sup>
     70 
     71 #### Case 1: Using `setuid` with `system`
     72 
     73 **Objective**: Understanding the effect of `setuid` in combination with `system` and `bash` as `sh`.
     74 
     75 **C Code**:
     76 
     77 ```c
     78 #define _GNU_SOURCE
     79 #include <stdlib.h>
     80 #include <unistd.h>
     81 
     82 int main(void) {
     83     setuid(1000);
     84     system("id");
     85     return 0;
     86 }
     87 ```
     88 
     89 **Compilation and Permissions:**
     90 
     91 ```bash
     92 oxdf@hacky$ gcc a.c -o /mnt/nfsshare/a;
     93 oxdf@hacky$ chmod 4755 /mnt/nfsshare/a
     94 ```
     95 
     96 ```bash
     97 bash-4.2$ $ ./a
     98 uid=99(nobody) gid=99(nobody) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0
     99 ```
    100 
    101 **Analysis:**
    102 
    103 - `ruid` and `euid` start as 99 (nobody) and 1000 (frank) respectively.
    104 - In this unprivileged context, `setuid(1000)` leaves `ruid` at 99 and `euid` at 1000.<sup>[[1]](#references)</sup>
    105 - `system` executes `/bin/bash -c id` due to the symlink from sh to bash.
    106 - `bash`, without `-p`, adjusts `euid` to match `ruid`, resulting in both being 99 (nobody).<sup>[[1]](#references)</sup>
    107 
    108 #### Case 2: Using setreuid with system
    109 
    110 **C Code**:
    111 
    112 ```c
    113 #define _GNU_SOURCE
    114 #include <stdlib.h>
    115 #include <unistd.h>
    116 
    117 int main(void) {
    118     setreuid(1000, 1000);
    119     system("id");
    120     return 0;
    121 }
    122 ```
    123 
    124 **Compilation and Permissions:**
    125 
    126 ```bash
    127 oxdf@hacky$ gcc b.c -o /mnt/nfsshare/b; chmod 4755 /mnt/nfsshare/b
    128 ```
    129 
    130 **Execution and Result:**
    131 
    132 ```bash
    133 bash-4.2$ $ ./b
    134 uid=1000(frank) gid=99(nobody) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0
    135 ```
    136 
    137 **Analysis:**
    138 
    139 - `setreuid` sets both ruid and euid to 1000.
    140 - `system` invokes bash, which maintains the user IDs due to their equality, effectively operating as frank.<sup>[[1]](#references)</sup>
    141 
    142 #### Case 3: Using setuid with execve
    143 
    144 Objective: Exploring the interaction between setuid and execve.
    145 
    146 ```bash
    147 #define _GNU_SOURCE
    148 #include <stdlib.h>
    149 #include <unistd.h>
    150 
    151 int main(void) {
    152     setuid(1000);
    153     execve("/usr/bin/id", NULL, NULL);
    154     return 0;
    155 }
    156 ```
    157 
    158 **Execution and Result:**
    159 
    160 ```bash
    161 bash-4.2$ $ ./c
    162 uid=99(nobody) gid=99(nobody) euid=1000(frank) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0
    163 ```
    164 
    165 **Analysis:**
    166 
    167 - `ruid` remains 99, but euid is set to 1000, in line with setuid's effect.<sup>[[1]](#references)</sup>
    168 
    169 **C Code Example 2 (Calling Bash):**
    170 
    171 ```bash
    172 #define _GNU_SOURCE
    173 #include <stdlib.h>
    174 #include <unistd.h>
    175 
    176 int main(void) {
    177     setuid(1000);
    178     execve("/bin/bash", NULL, NULL);
    179     return 0;
    180 }
    181 ```
    182 
    183 **Execution and Result:**
    184 
    185 ```bash
    186 bash-4.2$ $ ./d
    187 bash-4.2$ $ id
    188 uid=99(nobody) gid=99(nobody) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0
    189 ```
    190 
    191 **Analysis:**
    192 
    193 - Although `euid` is set to 1000 by `setuid`, `bash` resets euid to `ruid` (99) due to the absence of `-p`.<sup>[[1]](#references)</sup>
    194 
    195 **C Code Example 3 (Using bash -p):**
    196 
    197 ```bash
    198 #define _GNU_SOURCE
    199 #include <stdlib.h>
    200 #include <unistd.h>
    201 
    202 int main(void) {
    203     char *const paramList[10] = {"/bin/bash", "-p", NULL};
    204     setuid(1000);
    205     execve(paramList[0], paramList, NULL);
    206     return 0;
    207 }
    208 ```
    209 
    210 **Execution and Result:**
    211 
    212 ```bash
    213 bash-4.2$ $ ./e
    214 bash-4.2$ $ id
    215 uid=99(nobody) gid=99(nobody) euid=1000(frank)
    216 ```
    217 
    218 ## References
    219 
    220 - [1] [SetUID Rabbit Hole - 0xdf](https://0xdf.gitlab.io/2022/05/31/setuid-rabbithole.html#testing-on-jail)
    221 - [2] [man7.org - setuid man page](https://man7.org/linux/man-pages/man2/setuid.2.html)
    222 - [3] [man7.org - setresuid man page](https://man7.org/linux/man-pages/man2/setresuid.2.html)
    223 - [4] [man7.org - setreuid man page](https://man7.org/linux/man-pages/man2/setreuid.2.html)
    224 - [5] [man7.org - execve man page](https://man7.org/linux/man-pages/man2/execve.2.html)
    225 - [6] [man7.org - system man page](https://man7.org/linux/man-pages/man3/system.3.html)
    226 - [7] [man7.org - bash man page](https://man7.org/linux/man-pages/man1/bash.1.html)
    227 - [8] [man7.org - POSIX sh man page](https://man7.org/linux/man-pages/man1/sh.1p.html)