euid, ruid, suid - HackTricks

# euid, ruid, suid

> [!TIP] Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
> Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

> Support HackTricks
> - Check the subscription plans!
> - Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
> - Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.

### User Identification Variables

- `ruid`: The real user ID denotes the user who initiated the process.
- `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, where `euid` assumes the file owner's identity, thus granting specific operational permissions.
- `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.

#### Important Note

A process not operating under root can only modify its `euid` to match the current `ruid`, `euid`, or `suid`.

### Understanding set*uid Functions

- `setuid`: Contrary to initial assumptions, `setuid` primarily modifies `euid` rather than `ruid`. Specifically, for privileged processes, it aligns `ruid`, `euid`, and `suid` with the specified user, often root, effectively solidifying these IDs due to the overriding `suid`. Detailed insights can be found in the setuid man page.
- `setreuid` and `setresuid`: These functions allow for the nuanced adjustment of `ruid`, `euid`, and `suid`. However, their capabilities are contingent on the process's privilege level. For non-root processes, modifications are restricted to the current values of `ruid`, `euid`, and `suid`. In contrast, root processes or those with `CAP_SETUID` capability can assign arbitrary values to these IDs. More information can be gleaned from the setresuid man page and the setreuid man page.

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.

Notably, while `setuid` might be a common go-to for privilege elevation to root (since it aligns all IDs to root), differentiating between these functions is crucial for understanding and manipulating user ID behaviors in varying scenarios.

### Program Execution Mechanisms in Linux

#### `execve` System Call

- Functionality: `execve` initiates a program, determined by the first argument. It takes two array arguments, `argv` for arguments and `envp` for the environment.
- 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.
- User ID Preservation:
  - `ruid`, `euid`, and supplementary group IDs remain unaltered.
  - `euid` might have nuanced changes if the new program has the SetUID bit set.
  - `suid` gets updated from `euid` post-execution.
- Documentation: Detailed information can be found on the `execve` man page.

#### `system` Function

- Functionality: Unlike `execve`, `system` creates a child process using `fork` and executes a command within that child process using `execl`.
- Command Execution: Executes the command via `sh` with `execl("/bin/sh", "sh", "-c", command, (char *) NULL);`.
- Behavior: As `execl` is a form of `execve`, it operates similarly but in the context of a new child process.
- Documentation: Further insights can be obtained from the `system` man page.

#### Behavior of `bash` and `sh` with SUID

- `bash`:
  - Has a `-p` option influencing how `euid` and `ruid` are treated.
  - Without `-p`, `bash` sets `euid` to `ruid` if they initially differ.
  - With `-p`, the initial `euid` is preserved.
  - More details can be found on the `bash` man page.
- `sh`:
  - Does not possess a mechanism similar to `-p` in `bash`.
  - The behavior concerning user IDs is not explicitly mentioned, except under the `-i` option, emphasizing the preservation of `euid` and `ruid` equality.
  - Additional information is available on the `sh` man page.

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.

### Testing User ID Behaviors in Executions

Examples taken from https://0xdf.gitlab.io/2022/05/31/setuid-rabbithole.html#testing-on-jail, check it for further information

#### Case 1: Using `setuid` with `system`

Objective: Understanding the effect of `setuid` in combination with `system` and `bash` as `sh`.

C Code:

    #define _GNU_SOURCE
    #include <stdlib.h>
    #include <unistd.h>

    int main(void) {
        setuid(1000);
        system("id");
        return 0;
    }

Compilation and Permissions:

    oxdf@hacky$ gcc a.c -o /mnt/nfsshare/a;
    oxdf@hacky$ chmod 4755 /mnt/nfsshare/a

    bash-4.2$ $ ./a
    uid=99(nobody) gid=99(nobody) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0

Analysis:

- `ruid` and `euid` start as 99 (nobody) and 1000 (frank) respectively.
- `setuid` aligns both to 1000.
- `system` executes `/bin/bash -c id` due to the symlink from sh to bash.
- `bash`, without `-p`, adjusts `euid` to match `ruid`, resulting in both being 99 (nobody).

#### Case 2: Using setreuid with system

C Code:

    #define _GNU_SOURCE
    #include <stdlib.h>
    #include <unistd.h>

    int main(void) {
        setreuid(1000, 1000);
        system("id");
        return 0;
    }

Compilation and Permissions:

    oxdf@hacky$ gcc b.c -o /mnt/nfsshare/b; chmod 4755 /mnt/nfsshare/b

Execution and Result:

    bash-4.2$ $ ./b
    uid=1000(frank) gid=99(nobody) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0

Analysis:

- `setreuid` sets both ruid and euid to 1000.
- `system` invokes bash, which maintains the user IDs due to their equality, effectively operating as frank.

#### Case 3: Using setuid with execve

Objective: Exploring the interaction between setuid and execve.

    #define _GNU_SOURCE
    #include <stdlib.h>
    #include <unistd.h>

    int main(void) {
        setuid(1000);
        execve("/usr/bin/id", NULL, NULL);
        return 0;
    }

Execution and Result:

    bash-4.2$ $ ./c
    uid=99(nobody) gid=99(nobody) euid=1000(frank) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0

Analysis:

- `ruid` remains 99, but euid is set to 1000, in line with setuid's effect.

C Code Example 2 (Calling Bash):

    #define _GNU_SOURCE
    #include <stdlib.h>
    #include <unistd.h>

    int main(void) {
        setuid(1000);
        execve("/bin/bash", NULL, NULL);
        return 0;
    }

Execution and Result:

    bash-4.2$ $ ./d
    bash-4.2$ $ id
    uid=99(nobody) gid=99(nobody) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0

Analysis:

- Although `euid` is set to 1000 by `setuid`, `bash` resets euid to `ruid` (99) due to the absence of `-p`.

C Code Example 3 (Using bash -p):

    #define _GNU_SOURCE
    #include <stdlib.h>
    #include <unistd.h>

    int main(void) {
        char *const paramList[10] = {"/bin/bash", "-p", NULL};
        setuid(1000);
        execve(paramList[0], paramList, NULL);
        return 0;
    }

Execution and Result:

    bash-4.2$ $ ./e
    bash-4.2$ $ id
    uid=99(nobody) gid=99(nobody) euid=100

## References

- https://0xdf.gitlab.io/2022/05/31/setuid-rabbithole.html#testing-on-jail

> [!TIP] Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
> Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

> Support HackTricks
> - Check the subscription plans!
> - Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
> - Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.