7.6 KiB
euid, ruid, suid
{{#include ../../banners/hacktricks-training.md}}
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,euidmirrorsruid, barring instances like a SetUID binary execution, whereeuidassumes 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,setuidprimarily modifieseuidrather thanruid. Specifically, for privileged processes, it alignsruid,euid, andsuidwith the specified user, often root, effectively solidifying these IDs due to the overridingsuid. Detailed insights can be found in the setuid man page.setreuidandsetresuid: These functions allow for the nuanced adjustment ofruid,euid, andsuid. However, their capabilities are contingent on the process's privilege level. For non-root processes, modifications are restricted to the current values ofruid,euid, andsuid. In contrast, root processes or those withCAP_SETUIDcapability 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:
execveinitiates a program, determined by the first argument. It takes two array arguments,argvfor arguments andenvpfor 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.euidmight have nuanced changes if the new program has the SetUID bit set.suidgets updated fromeuidpost-execution.
- Documentation: Detailed information can be found on the
execveman page.
system Function
- Functionality: Unlike
execve,systemcreates a child process usingforkand executes a command within that child process usingexecl. - Command Execution: Executes the command via
shwithexecl("/bin/sh", "sh", "-c", command, (char *) NULL);. - Behavior: As
execlis a form ofexecve, it operates similarly but in the context of a new child process. - Documentation: Further insights can be obtained from the
systemman page.
Behavior of bash and sh with SUID
bash:- Has a
-poption influencing howeuidandruidare treated. - Without
-p,bashsetseuidtoruidif they initially differ. - With
-p, the initialeuidis preserved. - More details can be found on the
bashman page.
- Has a
sh:- Does not possess a mechanism similar to
-pinbash. - The behavior concerning user IDs is not explicitly mentioned, except under the
-ioption, emphasizing the preservation ofeuidandruidequality. - Additional information is available on the
shman page.
- Does not possess a mechanism similar to
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:
ruidandeuidstart as 99 (nobody) and 1000 (frank) respectively.setuidaligns both to 1000.systemexecutes/bin/bash -c iddue to the symlink from sh to bash.bash, without-p, adjustseuidto matchruid, 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:
setreuidsets both ruid and euid to 1000.systeminvokes 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:
ruidremains 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
euidis set to 1000 bysetuid,bashresets euid toruid(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
{{#include ../../banners/hacktricks-training.md}}