hacktricks/linux-hardening/privilege-escalation/euid-ruid-suid.md

9.8 KiB
Raw Blame History

euid, ruid, suid

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

用户身份变量

  • ruid: 真实用户ID表示启动进程的用户。
  • euid: 作为有效用户ID,它代表系统用来确定进程权限的用户身份。通常,euidruid相同除非执行SetUID二进制文件时euid会承担文件所有者的身份,从而授予特定的操作权限。
  • suid: 保存的用户ID在高权限进程通常以root身份运行需要临时放弃其权限以执行某些任务然后再重新获得其最初的提升状态时至关重要。

重要说明

非root操作的进程只能将其euid修改为当前的ruideuidsuid

理解set*uid函数

  • setuid: 与最初的假设相反,setuid主要修改的是euid而不是ruid。具体来说,对于有权限的进程,它会将ruideuidsuid与指定的用户通常是root对齐由于覆盖了suid这些ID因此变得固定。更多详细信息可以在setuid手册页中找到。
  • setreuidsetresuid: 这些函数允许对ruideuidsuid进行微妙的调整。然而它们的能力取决于进程的权限级别。对于非root进程修改限于ruideuidsuid的当前值。相比之下root进程或具有CAP_SETUID能力的进程可以为这些ID分配任意值。更多信息可以从setresuid手册页setreuid手册页中获得。

这些功能的设计不是作为安全机制而是为了促进预期的操作流程例如当程序通过改变其有效用户ID来采用另一个用户的身份时。

值得注意的是,虽然setuid可能是提升到root权限的常用方法因为它将所有ID对齐到root但区分这些函数对于理解和操纵不同场景中的用户ID行为至关重要。

Linux中的程序执行机制

execve 系统调用

  • 功能: execve通过第一个参数确定程序,并启动它。它接受两个数组参数,argv用于参数,envp用于环境。
  • 行为: 它保留调用者的内存空间,但刷新堆栈、堆和数据段。程序的代码被新程序替换。
  • 用户ID保留:
  • ruideuid和补充组ID保持不变。
  • 如果新程序设置了SetUID位euid可能会有细微变化。
  • 执行后,suideuid更新。
  • 文档: 更多详细信息可以在execve手册页中找到。

system 函数

  • 功能: 与execve不同,system使用fork创建子进程,并使用execl在该子进程中执行命令。
  • 命令执行: 通过sh执行命令,使用execl("/bin/sh", "sh", "-c", command, (char *) NULL);
  • 行为: 由于execlexecve的一种形式,它以类似的方式操作,但在新子进程的上下文中。
  • 文档: 更多洞见可以从system手册页中获得。

带有SUID的bashsh的行为

  • bash:
  • 有一个-p选项,影响euidruid的处理方式。
  • 没有-p时,如果euidruid最初不同,bash会将euid设置为ruid
  • 使用-p时,保留初始euid
  • 更多细节可以在bash手册页中找到。
  • sh:
  • 没有类似于bash中的-p机制。
  • 关于用户ID的行为没有明确提及除了在-i选项下,强调保持euidruid的相等。
  • 额外信息可以在sh手册页中找到。

这些机制在操作上各不相同为执行和转换程序提供了多样化的选择具有在如何管理和保留用户ID方面的特定细微差别。

测试执行中的用户ID行为

示例取自https://0xdf.gitlab.io/2022/05/31/setuid-rabbithole.html#testing-on-jail更多信息请查看该网站

案例1结合setuidsystem使用

目标: 理解结合system和作为shbashsetuid的效果。

C代码:

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

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

编译和权限:

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

分析:

  • ruideuid 分别以 99 (nobody) 和 1000 (frank) 开始。
  • setuid 将两者都对齐到 1000。
  • system 通过从 sh 到 bash 的符号链接执行 /bin/bash -c id
  • bash,如果没有 -p,会调整 euid 以匹配 ruid,导致两者都变为 99 (nobody)。

案例 2: 使用 setreuid 与 system

C 代码:

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

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

编译和权限:

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

执行和结果:

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 相等bash 保持这些 ID有效地作为 frank 操作。

案例 3结合 setuid 与 execve 使用

目标:探索 setuid 与 execve 之间的交互。

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

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

执行和结果:

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

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

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

执行和结果:

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

分析:

  • 尽管 setuideuid 设置为1000但由于缺少 -pbash 将 euid 重置为 ruid (99)。
#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-4.2$ $ ./e
bash-4.2$ $ id
uid=99(nobody) gid=99(nobody) euid=100

参考资料

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥