I Was Wrong About sudo. Let’s Talk about SetUID#
I thought I had a decent handle on Linux permissions users, groups, the classic rwx bits. Then I came across the pwn.college program misuse module.
While working on a privilege escalation problem, something worked that just shouldn’t have, based on the mental model I was using at the time. It forced me to see that my whole idea of sudo was wrong.
I always saw sudo as some magic keyword built into the OS. Well turns out it’s not. The real power is a much simpler, older concept called SetUID.SetUID is basically all you need to understand how this stuff really works.
TL;DR#
sudois just a program that usessetuid: It isn’t a magic OS keyword. It’s a normal program owned byrootthat has thesetuidpermission bit. It uses its ownsetuid-given power to run your commands asroot.setuidis automatic privilege elevation: Thesetuidbit (thesinls -l) on any program simply means: “Run this as the file’s owner, not the person who executed it.” Nosudocommand needed. Ifrootowns the file, the program runs asroot.Your “Effective” ID changes, not your “Real” one: The kernel tracks who you really are (Real UID) versus what you can currently do (Effective UID).
setuidonly changes your Effective UID for that one process, and that’s the ID the OS checks for file permissions.This power is persistent (and dangerous): The elevated privilege lasts for the entire time the program is running. It doesn’t just turn off automatically, which is why poorly written
setuidprograms can be a huge security risk.Note : For security, most Linux systems mount temporary directories like
/tmpwith thenosuidmount option. This rule tells the kernel to completely ignore theSetUIDbit for any program on that entire filesystem. It’s a security control to stop an attacker from runningSetUIDfrom this location. You can check this yourself by runningmount | grep /tmp.
Back to the Basics: How do processes normally get Permissions#
On Linux, everything is about access control. The OS checks user and group IDs (UID and GID) to decide who can do what. When a program runs, it’s just a set of instructions spawning various processes, and that process needs permissions to do its job (like read a file or open a network connection).
Here’s the normal rule: Every process inherits the user and group ID of the parent process that spawned it. When, user john, runs a command, it runs with john’s UID and GID.
Privilege Escalation is a FEATURE not a de facto vulnerability as I was thinking#
This is where my first big “aha!” moment happened. Privilege Escalation (PE) isn’t inherently a vulnerability. At its core, it’s a mechanism that allows a user to perform actions that only a different user has the permissions for. It only becomes a vulnerability when it is misconfigured.
On Linux, this mechanism is primarily implemented through two tools we often see: SetUID and sudo. And as you will soon learn, the latter is completely dependent on the former.
SetUID#
SetUID (Set User ID) is a special permission bit. It has an octal value of 4 and is represented by an s in the file owner’s permission block (-rwsr-xr-x).
Its function is simple but powerful: it allows anyone with execute permissions on the file to run that program with the power(permissions) of the file’s owner.
Here’s how the process works step-by-step:
- A user starts a program that has the SetUID bit set.
- When the Linux kernel sees the
sbit, it breaks the normal inheritance rule. - Instead of using the user’s ID, the kernel sets the process’s effective user ID (euid) to the ID of the user who owns the file.
- If not explicitly changed in the code, the program continues to run with the owner’s privileges until it terminates.
So, What Is sudo Then ?#
sudo is not a magic do everything command it is just a normal C program located at /usr/bin/sudo that is owned by root and has the SetUID bit set.
Its behavior is an exact, but much more secure, implementation of the SetUID mechanism. Here is what really happens when you type sudo apt update:
- You execute
/usr/bin/sudo, which starts up with an effective user ID ofrootbecause of the SetUID bit. - But
sudodoesn’t just run your command. It’s a security mechanism put in place to control and keep track of command running with high privilegies. First, it uses its root power to read the/etc/sudoersfile—a file onlyrootcan read. - It checks if you, the real user, is listed in that file and have permission to run
apt update. - If the rules match, it may ask for your password to verify your identity.
- Only if all these checks pass does
sudothen spawn a child process to runapt update, which now inherits therootcontext from its parent,sudo.
DIY#
Not being a system programmer I asked gemini for a modified version of the cat command that prints the ruid,euid,gid and egid of the person runing the program along with the content of the file.
- Copy the code below into a file, for example:
simplecat.c
1```c
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <sys/types.h>
6#include <unistd.h> // For getuid() and geteuid()
7
8int main(int argc, char *argv[]) {
9 FILE *fp;
10 char buffer[1024];
11
12 // Print Real and Effective UIDs to observe the change
13 printf("Real UID: %d\n", getuid());
14 printf("Effective UID: %d\n", geteuid());
15 printf("Real GID: %d\n", getgid());
16 printf("Effective GID: %d\n", getegid());
17
18 if (argc < 2) {
19 fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
20 return 1;
21 }
22
23 fp = fopen(argv[1], "r");
24 if (fp == NULL) {
25 perror("Error opening file");
26 return 1;
27 }
28
29 while (fgets(buffer, sizeof(buffer), fp) != NULL) {
30 printf("%s", buffer);
31 }
32
33 fclose(fp);
34 return 0;
35}
36```compile the program
gcc simplecat.c -o mycatYou can try this by compiling two versions of this program and set the
setuidbit for one and the other one without thesetuidbit. The latter will be run usingsudoto access. Create two distinctuserswith differentpermissionsand try to access afilebased on thepermissionsyou have set, and note the behaviour when using either thesudo mycatcommand or the permission from thesetuidbit
Note : For security, most Linux distributions mount temporary directories like /tmp with the nosuid mount option. This rule tells the kernel to completely ignore the SetUID bit for any program on that entire filesystem. It’s a security control to stop an attacker from running SetUID from this location.
You can check this yourself by running mount | grep /tmp.
That is it. Thanks for reading
I hope you learned something from this post. Any suggestions or areas of improvement? Please let me know, it will help me a lot to write better future posts. Thanks !!