上一篇讲了 rwx 这套基本权限位。但 ls -l 里偶尔会冒出 rws、rwt 这种带字母 s 和 t 的位置,很多人第一次看到就懵了——这不是执行位吗,怎么变成小写字母了?这就是 Linux 的三个特殊权限位:SUID、SGID、Sticky Bit。它们在基本权限的"三位一组"之上再加一层,解决的是普通 rwx 解决不了的问题:让普通用户临时借程序属主的身份干活、让共享目录新建的文件自动继承组、让 /tmp 里人人能写却互删不了文件。
一、特殊权限在权限位里长什么样
特殊权限占权限数字的第四位(千位),和 rwx 一样可以用数字和符号两种方式表示:
| SUID | 4 | 属主的 x 位 | s(有 x 时)/ S(无 x 时) |
| SGID | 2 | 属组的 x 位 | s(有 x 时)/ S(无 x 时) |
| Sticky Bit | 1 | 其他人的 x 位 | t(有 x 时)/ T(无 x 时) |
注意大小写:小写 s/t 表示原 x 位存在,叠加特殊权限;大写 S/T 表示原 x 位是空的,特殊权限加上去但执行位没开——后者通常是配置错误。
先看两个现成的例子,系统自带的:
ls -l /usr/bin/passwd /tmp
预期输出:
-rwsr-xr-x 1 root root 68156 Jul 1 2023 /usr/bin/passwd
drwxrwxrwt 10 root root 4096 Sep 16 10:30 /tmp
passwd 的属主是 root,属主权限里那个 s 就是 SUID;/tmp 其他人权限位上的 t 就是 Sticky Bit。
二、SUID:让执行者临时拥有属主身份
原理
正常情况下,一个进程的权限等于执行者的身份。但给可执行文件挂上 SUID 后,任何人运行它时,进程的 UID 会临时变成文件属主的 UID,而不是运行者自己的 UID。
最典型的例子就是 passwd。普通用户要改自己的密码,密码存在 /etc/shadow 里,而这个文件只有 root 能写:
ls -l /etc/shadow
预期输出:
-rw-r—– 1 root shadow 1234 Sep 16 09:00 /etc/shadow
普通用户直接改 shadow 肯定没权限。passwd 命令靠 SUID 让自己运行时临时变成 root,改完 shadow 再把权限降回来,整个过程对用户透明。
设置与取消
chmod u+s 文件名 # 加 SUID
chmod u-s 文件名 # 去掉
chmod 4755 文件名 # 数字法:千位 4
做个实验,看进程身份怎么变:
sudo chown root:root /tmp/checkid
sudo chmod 4755 /tmp/checkid
ls -l /tmp/checkid
预期输出:
-rwsr-xr-x 1 root root 1024 Sep 16 11:00 /tmp/checkid
普通用户 zhangsan 运行它,进程的有效 UID 会变成 0(root),尽管启动者是 1001。
⚠️ 安全风险
SUID 是一把双刃剑。任何被挂上 SUID 的可执行文件,都是提权攻击的潜在入口。攻击者找到一个有 SUID 的、能调用 shell 的程序(比如旧版 find、nmap –interactive、vim),就能直接拿到 root shell。
运维上两件事要做:
预期输出(节选):/usr/bin/sudo
/usr/bin/passwd
/usr/bin/su
/usr/bin/mount
/usr/bin/umount
这些是系统正常的。如果业务目录里冒出来一个你不认识的 SUID 文件,就要查了。
三、SGID:让共享目录继承组,让程序继承属组身份
SGID 有两个完全不同的作用,看它挂在文件上还是目录上。
挂在可执行文件上
和 SUID 类似,只不过变的是组 ID:运行进程时,有效 GID 变成文件属组的 GID。实际用得很少,知道有这回事就行。
挂在目录上(重点)
这是共享目录的标准做法。给一个目录挂上 SGID 后,任何人在这个目录里新建的文件/子目录,属组都会自动继承目录的属组,而不是新建者自己的主组。
场景:开发组共用 /data/dev,希望所有人往里建的文件组都是 developers,互相能改。
sudo groupadd developers
sudo chown root:developers /data/dev
sudo chmod 2775 /data/dev # 千位 2 = SGID
ls -ld /data/dev
预期输出:
drwxrwsr-x 2 root developers 4096 Sep 16 11:10 /data/dev
注意属组权限位上的 s。现在 zhangsan 和 lisi 都把自己加进 developers 组,往里建文件:
sudo su – zhangsan
touch /data/dev/zhangsan_note.txt
ls -l /data/dev/
预期输出:
-rw-rw-r– 1 zhangsan developers 0 Sep 16 11:12 zhangsan_note.txt
文件的属组自动是 developers,而不是 zhangsan 的私有组。lisi 再建文件也是同一个组,配合 g+w,两人互相能改——团队共享目录的标准配方就是 chgrp + chmod 2775 + umask 002。
设置与取消
chmod g+s 目录 # 加 SGID
chmod g-s 目录 # 去掉
chmod 2755 目录 # 数字法
四、Sticky Bit:/tmp 人人能写但互删不了
问题
回到开头那个矛盾:/tmp 要所有人可写(o+w),否则大家没法往临时目录里放东西。但如果只是普通的 0777,任何用户都能删掉别人的临时文件——因为删除文件看的是目录的 w 权限,不是文件本身的权限。
Sticky Bit 就是来补这个漏洞的:给目录挂上 Sticky Bit 后,里面的文件只有属主(或 root)能删/改名,其他人就算对目录有 w 也不行。
设置与取消
chmod o+t 目录 # 加 Sticky Bit
chmod o-t 目录 # 去掉
chmod 1777 目录 # 数字法:千位 1
看 /tmp:
ls -ld /tmp
预期输出:
drwxrwxrwt 10 root root 4096 Sep 16 10:30 /tmp
末尾的 t 就是 Sticky Bit。zhangsan 往 /tmp 放个文件,lisi 就算能进 /tmp,也删不掉它——只能删自己建的。
什么时候该用
凡是"多人共享、人人可写"的目录,除了 /tmp,还有 FTP 上传区、共享上传目录,都建议挂 Sticky Bit。忘了挂,迟早出事故。
五、三个特殊权限速查表
| SUID | 4 | 可执行文件 | 普通用户临时借属主身份运行(passwd、sudo) |
| SGID | 2 | 目录 | 新建文件自动继承目录属组(团队共享目录) |
| SGID | 2 | 可执行文件 | 临时借属组身份(少见) |
| Sticky Bit | 1 | 目录 | 共享目录里只能删自己的文件(/tmp) |
chmod 的四位数字从左到右就是 SUID SGID Sticky 基本权限,比如 4755、2755、1777。
⚠️ 常见错误
六、知识扩展:SUID 提权背后的内核机制
SUID 之所以能工作,是因为 Linux 把"身份"拆成了两套:实际 ID(real ID) 和有效 ID(effective ID)。
- 实际 ID:你是谁,记录在进程的 uid 字段,来自登录账号。
- 有效 ID:内核做权限检查时实际参考的那个 ID,记录在 euid。
平时两者相等。SUID 程序启动时,内核把 euid 设成文件属主的 uid,于是文件读写、capability 检查都按 root 算。程序结束后退出,这个提升不残留。
passwd 正是利用这一点:它本身 setuid root,打开 shadow 写入新密码,然后立刻用 setuid(getuid()) 把自己降回普通用户身份再去做其他事。这种"用完就降权"的写法是安全编程的基本要求——只在需要 root 的那一小段持有 root。
反过来,安全扫描工具查 SUID 提权漏洞,本质上就是在枚举:哪些 SUID 程序的 euid=0 阶段能被诱导去执行攻击者指定的命令(比如传一个 –exec 参数)。掌握了 real/effective 这层模型,看 Privilege Escalation 类文章时就不会被一堆名词绕晕。




