192 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# euid, ruid, suid
{{#include ../../banners/hacktricks-training.md}}
### 用户标识变量
- **`ruid`**: **真实用户 ID** 表示发起该进程的用户。
- **`euid`**: 被称为 **有效用户 ID**,它代表系统用来确定进程权限的用户身份。通常情况下,`euid``ruid` 相同,除非在执行 SetUID 二进制文件的情况下,`euid` 会采用文件所有者的身份,从而授予特定的操作权限。
- **`suid`**: 这个 **保存的用户 ID** 在高权限进程(通常以 root 身份运行)需要暂时放弃其权限以执行某些任务时至关重要,之后再恢复其最初的提升状态。
#### 重要说明
非 root 进程只能将其 `euid` 修改为当前的 `ruid``euid``suid`
### 理解 set\*uid 函数
- **`setuid`**: 与最初的假设相反,`setuid` 主要修改 `euid` 而不是 `ruid`。具体而言,对于特权进程,它将 `ruid``euid``suid` 与指定用户(通常是 root对齐有效地巩固这些 ID因为 `suid` 的覆盖。详细信息可以在 [setuid man page](https://man7.org/linux/man-pages/man2/setuid.2.html) 中找到。
- **`setreuid`** 和 **`setresuid`**: 这些函数允许对 `ruid``euid``suid` 进行细致的调整。然而,它们的能力取决于进程的权限级别。对于非 root 进程,修改仅限于当前的 `ruid``euid``suid` 值。相比之下root 进程或具有 `CAP_SETUID` 能力的进程可以为这些 ID 分配任意值。更多信息可以从 [setresuid man page](https://man7.org/linux/man-pages/man2/setresuid.2.html) 和 [setreuid man page](https://man7.org/linux/man-pages/man2/setreuid.2.html) 中获取。
这些功能的设计并不是作为安全机制,而是为了促进预期的操作流程,例如当程序通过更改其有效用户 ID 来采用另一个用户的身份时。
值得注意的是,虽然 `setuid` 可能是提升到 root 权限的常用方法(因为它将所有 ID 对齐到 root但区分这些函数对于理解和操控不同场景下的用户 ID 行为至关重要。
### Linux 中的程序执行机制
#### **`execve` 系统调用**
- **功能**: `execve` 启动一个程序,由第一个参数决定。它接受两个数组参数,`argv` 用于参数,`envp` 用于环境。
- **行为**: 它保留调用者的内存空间,但刷新堆栈、堆和数据段。程序的代码被新程序替换。
- **用户 ID 保持**:
- `ruid``euid` 和附加的组 ID 保持不变。
- 如果新程序设置了 SetUID 位,`euid` 可能会有细微变化。
- `suid` 在执行后从 `euid` 更新。
- **文档**: 详细信息可以在 [`execve` man page](https://man7.org/linux/man-pages/man2/execve.2.html) 中找到。
#### **`system` 函数**
- **功能**: 与 `execve` 不同,`system` 使用 `fork` 创建一个子进程,并在该子进程中执行命令,使用 `execl`
- **命令执行**: 通过 `sh` 执行命令,使用 `execl("/bin/sh", "sh", "-c", command, (char *) NULL);`
- **行为**: 由于 `execl``execve` 的一种形式,它在新子进程的上下文中以类似方式操作。
- **文档**: 进一步的见解可以从 [`system` man page](https://man7.org/linux/man-pages/man3/system.3.html) 中获得。
#### **带有 SUID 的 `bash` 和 `sh` 的行为**
- **`bash`**:
- 具有 `-p` 选项,影响 `euid``ruid` 的处理方式。
- 如果没有 `-p``bash` 会将 `euid` 设置为 `ruid`,如果它们最初不同。
- 如果有 `-p`,则保留初始的 `euid`
- 更多细节可以在 [`bash` man page](https://linux.die.net/man/1/bash) 中找到。
- **`sh`**:
- 不具备类似于 `bash` 中的 `-p` 的机制。
- 关于用户 ID 的行为没有明确提及,除了在 `-i` 选项下,强调保持 `euid``ruid` 的相等性。
- 额外信息可在 [`sh` man page](https://man7.org/linux/man-pages/man1/sh.1p.html) 中找到。
这些机制在操作上各不相同,提供了多种执行和程序间转换的选项,具体细节在用户 ID 的管理和保持方面有所不同。
### 测试执行中的用户 ID 行为
示例取自 https://0xdf.gitlab.io/2022/05/31/setuid-rabbithole.html#testing-on-jail查看以获取更多信息
#### 案例 1: 使用 `setuid` 和 `system`
**目标**: 理解 `setuid``system``bash` 作为 `sh` 结合的效果。
**C 代码**:
```c
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
int main(void) {
setuid(1000);
system("id");
return 0;
}
```
**编译和权限:**
```bash
oxdf@hacky$ gcc a.c -o /mnt/nfsshare/a;
oxdf@hacky$ chmod 4755 /mnt/nfsshare/a
```
```bash
bash-4.2$ $ ./a
uid=99(nobody) gid=99(nobody) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0
```
**分析:**
- `ruid``euid` 初始值分别为 99 (nobody) 和 1000 (frank)。
- `setuid` 将两者都调整为 1000。
- `system` 执行 `/bin/bash -c id`,这是由于 sh 到 bash 的符号链接。
- `bash` 在没有 `-p` 的情况下,将 `euid` 调整为与 `ruid` 匹配,导致两者都为 99 (nobody)。
#### 案例 2使用 setreuid 和 system
**C 代码**
```c
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
int main(void) {
setreuid(1000, 1000);
system("id");
return 0;
}
```
**编译和权限:**
```bash
oxdf@hacky$ gcc b.c -o /mnt/nfsshare/b; chmod 4755 /mnt/nfsshare/b
```
**执行和结果:**
```bash
bash-4.2$ $ ./b
uid=1000(frank) gid=99(nobody) groups=99(nobody) context=system_u:system_r:unconfined_service_t:s0
```
**分析:**
- `setreuid` 将 ruid 和 euid 都设置为 1000。
- `system` 调用 bash由于用户 ID 的相等性,保持用户 ID有效地作为 frank 操作。
#### 案例 3使用 setuid 和 execve
目标:探索 setuid 和 execve 之间的交互。
```bash
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
int main(void) {
setuid(1000);
execve("/usr/bin/id", NULL, NULL);
return 0;
}
```
**执行和结果:**
```bash
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
```
**分析:**
- `ruid` 保持为 99但 euid 被设置为 1000符合 setuid 的效果。
**C 代码示例 2调用 Bash**
```bash
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
int main(void) {
setuid(1000);
execve("/bin/bash", NULL, NULL);
return 0;
}
```
**执行和结果:**
```bash
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
```
**分析:**
- 尽管 `euid` 通过 `setuid` 设置为 1000`bash` 由于缺少 `-p``euid` 重置为 `ruid` (99)。
**C 代码示例 3 (使用 bash -p)**
```bash
#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;
}
```
**执行和结果:**
```bash
bash-4.2$ $ ./e
bash-4.2$ $ id
uid=99(nobody) gid=99(nobody) euid=100
```
## 参考
- [https://0xdf.gitlab.io/2022/05/31/setuid-rabbithole.html#testing-on-jail](https://0xdf.gitlab.io/2022/05/31/setuid-rabbithole.html#testing-on-jail)
{{#include ../../banners/hacktricks-training.md}}