12.2 SGID on Files: Run as the Group Owner
Right, so you’ve wrapped your head around SUID, which makes a process run as the user who owns the file. SGID is its slightly less famous, but arguably more useful, cousin. When you set the SGID bit on an executable file, it doesn’t change the user ID of the process—it changes the group ID. The process runs with the effective group permissions of the group that owns the file, not your primary group or any of your supplementary groups.
Think of it like this: you’re you (user1), but you’re temporarily deputized into the data_team group. You get all the access that group has, but only for the duration of that specific program’s execution. This is incredibly powerful for shared environments where a specific tool needs to read from or write to resources that are locked down to a particular group.
How to Set It (The Octal Way and The Symbolic Way)
You’ve got two ways to set this, just like with SUID. The octal method is my preferred one for scripts because it’s explicit. You’re setting the whole permission set at once. The 2 in the first digit of a four-digit octal permission is the SGID flag.
# The Octal Way: chmod 2xxx filename
chmod 2750 shared_tool.py
This command sets the permissions to rwxr-s---. Notice that s in the group execute field? That’s the visual indicator that the SGID bit is set. A capital S would indicate that the SGID bit is set but the group execute permission is not set—which is useless and a common pitfall we’ll get to in a minute.
The symbolic method is handy for quickly adding or removing the bit without recalculating all the permissions.
# The Symbolic Way: g+s
chmod g+s shared_tool.py
# To remove it
chmod g-s shared_tool.py
Why This Actually Matters: A Concrete Example
Let’s stop being abstract. Imagine a company where the dev_team group needs to run a script that appends logs to a single, critical log file, /var/log/critical_app.log. This log file is owned by root:dev_team with permissions rw-rw---- (660). This means only root and members of dev_team can write to it.
Now, imagine a junior developer, intern_bob, who is not in the dev_team group. He needs to run the logging script. Without SGID, this is a nightmare. You’d have to make the log file world-writable (a terrible idea), give intern_bob sudo rights to run the script as root (an even worse idea), or add him to the dev_team group (which might give him too much access).
SGID solves this elegantly. You make root:dev_team the owner of the script, set its permissions to 2750, and make it executable.
sudo chown root:dev_team /usr/local/bin/log_writer.sh
sudo chmod 2750 /usr/local/bin/log_writer.sh
ls -l /usr/local/bin/log_writer.sh
# -rwxr-s--- 1 root dev_team 123 Apr 10 11:45 /usr/local/bin/log_writer.sh
Now, when intern_bob runs log_writer.sh, the process doesn’t run with his effective group ID. It runs with the effective group ID of dev_team. Because the log file grants write access to the dev_team group, the script works perfectly for Bob. He gets just enough privilege to perform this one specific task and nothing more. It’s the principle of least privilege in action.
The Dreaded Capital ‘S’ and Other Pitfalls
Remember that capital S I mentioned? Let’s talk about that. If you see rwxr-S---, it means someone set the SGID bit on a file that is not executable by the group. This is completely pointless. The kernel can’t execute the file to create a process, so the SGID bit never even comes into play. It’s a dead bit. Always ensure the file is actually executable (chmod g+x) after setting SGID.
Another classic gotcha is forgetting that SGID only affects the effective group ID (EGID) of the process. The process still has all your other supplementary groups. Access checks are complex, and if any of your groups (real or effective) have access, you get in. The SGID-triggered EGID is just added to your list.
Best Practices and When to Use It
- Prefer SGID over SUID for group resource access: Needing to access group-owned resources is a far more common scenario than needing to impersonate another user. It’s also generally considered a smaller security risk.
- Audit religiously: Any script or binary with SGID set is a potential privilege escalation vector. You must know what every single one of them does. A
findcommand is your best friend here:# Find all SGID files on the system sudo find / -type f -perm -2000 2>/dev/null - Ownership matters: The file must be owned by the group whose privileges you want to borrow. An SGID binary owned by
root:rootis functionally identical to a normal binary, since your effective group would beroot, but your real group isn’t, so it rarely grants any new access. The power comes from the file being owned by a shared group you aren’t normally in. - It’s useless on shell scripts on most modern systems: Let’s be honest here. This is a big one. The Linux kernel, for very good security reasons, ignores the SUID and SGID bits on scripts that start with
#!/bin/bash. It only honors them on compiled binaries. If you need this functionality for a script, you need to create a small wrapper binary in C or use a tool likesudowith carefully configured rules. It’s a design choice that breaks a lot of beginner’s dreams but has probably saved us from countless security nightmares.