1.获得特权的两种方式
Linux中,特权任务(privileged tasks)可以通过两种方式执行:
-
Using SUID
-
Using capabilities
2.关于SUID
通过SUID拿到root权限,参考
https://blog.csdn.net/qq_39441603/article/details/125010004
https://blog.csdn.net/qq_39441603/article/details/124996277
3.关于Capabilities(权能)
在检查进程的权限时,EUID标识为0的进程被Linux认为是 root用户进程(特权进程),EUID标识为0的进程被Linux认为非特权进程。
特权进程通过所有内核权限检查,
非特权进程依据进程的EUID、EGID 和Supplementary Group等标识进行权限检查
SUID标识位虽然可以解决普通用户执行特权任务的问题,却带来了安全隐患:
某个设置了SUID标识位的可执行文件对应的进程,通常只需要root用户权限的一部分,但 SUID 给了它 root 用户的全部权限。 因此一旦某个设置了SUID标识位的可执行文件被非法利用,则所有root权限都被攻击者获得,提高了攻击者的能力。
Linux 引入了 capabilities 机制对 root 权限进行细分,实现按需授权。
3.1 Linux capabilities概念
将与 root用户 关联的权限划分为不同的单元,称为 Capability 。
Capability 可以作为进程的属性存在。每个Capability 可以独立启用和禁用。
检查进程权限的过程变成了:
- 如果进程的EUID是AID_ROOT,则进程拥有全部root权限
- 如果进程的EUID不是AID_ROOT,则检查进程是否有该操作所对应的 Capability
比如要向其他进程发送kill()信号,就得具有 Capability CAP_KILL; 比如要设置系统时间,就得具有 Capability CAP_SYS_TIME
如果有,则允许;否则拒绝操作(报错Operation not permitted
)
capability列表参见:
https://man7.org/linux/man-pages/man7/capabilities.7.html
https://www.cnblogs.com/sparkdev/p/11417781.html
capability 名称 | 描述 |
---|---|
CAP_AUDIT_CONTROL | 启用和禁用内核审计;改变审计过滤规则;检索审计状态和过滤规则 |
CAP_AUDIT_READ | 允许通过 multicast netlink 套接字读取审计日志 |
CAP_AUDIT_WRITE | 将记录写入内核审计日志 |
CAP_BLOCK_SUSPEND | 使用可以阻止系统挂起的特性 |
CAP_CHOWN | 修改文件所有者的权限 |
CAP_DAC_OVERRIDE | 忽略文件的 DAC 访问限制 |
CAP_DAC_READ_SEARCH | 忽略文件读及目录搜索的 DAC 访问限制 |
CAP_FOWNER | 忽略文件属主 ID 必须和进程用户 ID 相匹配的限制 |
CAP_FSETID | 允许设置文件的 setuid 位 |
CAP_IPC_LOCK | 允许锁定共享内存片段 |
CAP_IPC_OWNER | 忽略 IPC 所有权检查 |
CAP_KILL | 允许对不属于自己的进程发送信号 |
CAP_LEASE | 允许修改文件锁的 FL_LEASE 标志 |
CAP_LINUX_IMMUTABLE | 允许修改文件的 IMMUTABLE 和 APPEND 属性标志 |
CAP_MAC_ADMIN | 允许 MAC 配置或状态更改 |
CAP_MAC_OVERRIDE | 覆盖 MAC(Mandatory Access Control) |
CAP_MKNOD | 允许使用 mknod() 系统调用 |
CAP_NET_ADMIN | 允许执行网络管理任务 |
CAP_NET_BIND_SERVICE | 允许绑定到小于 1024 的端口 |
CAP_NET_BROADCAST | 允许网络广播和多播访问 |
CAP_NET_RAW | 允许使用原始套接字 |
CAP_SETGID | 允许改变进程的 GID |
CAP_SETFCAP | 允许为文件设置任意的 capabilities |
CAP_SETPCAP | 参考 capabilities man page |
CAP_SETUID | 允许改变进程的 UID |
CAP_SYS_ADMIN | 允许执行系统管理任务,如加载或卸载文件系统、设置磁盘配额等 |
CAP_SYS_BOOT | 允许重新启动系统 |
CAP_SYS_CHROOT | 允许使用 chroot() 系统调用 |
CAP_SYS_MODULE | 允许插入和删除内核模块 |
CAP_SYS_NICE | 允许提升优先级及设置其他进程的优先级 |
CAP_SYS_PACCT | 允许执行进程的 BSD 式审计 |
CAP_SYS_PTRACE | 允许跟踪任何进程 |
CAP_SYS_RAWIO | 允许直接访问 /devport、/dev/mem、/dev/kmem 及原始块设备 |
CAP_SYS_RESOURCE | 忽略资源限制 |
CAP_SYS_TIME | 允许改变系统时钟 |
CAP_SYS_TTY_CONFIG | 允许配置 TTY 设备 |
CAP_SYSLOG | 允许使用 syslog() 系统调用 |
CAP_WAKE_ALARM | 允许触发一些能唤醒系统的东西(比如 CLOCK_BOOTTIME_ALARM 计时器) |
3.2 getcap 命令和 setcap 命令
举例:
ping 命令在执行时需要访问网络,这就需要获得 root 权限。常规的做法是通过 SUID 实现,这里改用capability实现。
ping 文件所需的 capabilities 为 cap_net_admin 和 cap_net_raw,通过 setcap 命令可以添加它们:
sudo setcap cap_net_admin,cap_net_raw+ep /bin/ping
命令中的 ep 分别表示 Effective 和 Permitted 集合,+ 号表示把指定的 capabilities 添加到这些集合中(对于 Effective 来说是设置位)。
被赋予合适的 capabilities 后,ping 命令又可以正常工作了。
如果要移除刚才添加的 capabilities,可以使用:
sudo setcap cap_net_admin,cap_net_raw-ep /bin/ping
- 号表示从集合中移除(对于 Effective 来说是清除位)。
相比 SUID 方案,ping文件只具有必要的特权,在最大程度上减小了系统的安全攻击面。
使用sudo find / -perm /u=s
命令可以查找所有设置了setuid位的可执行文件。
3.3 进程的 capabilities与可执行文件的 capabilities
3.3.1 进程的 capabilities
对于进程来说,capabilities 是细分到线程的,即每个线程可以有自己的capabilities。
进程(线程)有5类 capabilities:
- Permitted(cap_permitted)
定义了进程能够拥有的 capabilities 的上限。- 不含CAP_SETPCAP的进程,只能往cap_inheritable中添加cap_permitted中有的capability。
- 不含CAP_SETPCAP的进程,只能丢掉cap_permitted中的capability,但不能向其中添加capability。
- 含CAP_SETPCAP的进程(UID为0),可以向cap_permitted中添加capability,但仍受规则约束。
- Effective(cap_effective)
当前进程实际拥有的权能。内核检查进程是否可以进行特权操作时,检查的便是 cap_effective数组。
- Inheritable (cap_inheritable)
父进程使用execve()执行可执行文件产生子进程时,子进程继承父进程的capability。
int execve(const char *pathname, char *const argv[],char*const envp[);
pathname :可执行文件的路径
argv :传递给程序的参数
envp∶传递给程序的新环境变量,无论是shell脚本,还是可执行文件都可以使用此环境变量
-
Bset(cap_bset)
用于限定进程从可执行文件中获取的capability。
-
Ambient
Linux 4.3 内核新增了一个 capabilities 集合叫 Ambient ,用来弥补 Inheritable 的不足。- 非特权用户如果在 Permitted 集合中有一个 capability,那么可以添加到 Ambient 集合中,这样它的子进程便可以在 Ambient、Permitted 和 Effective 集合中获取这个 capability。
3.3.2 可执行文件的 capabilities
可执行文件有三类 capabilities:
- Permitted:
在进程执行时,文件的Permitted Capabilites 自动被加入到进程的 Permitted Capabilites 中。 - Inheritable:
Inheritable 集合中的 capabilites 会与进程的 Inheritable 集合执行与操作,以确定进程在执行 execve 函数后哪些 capabilites 被继承。 - Effective:
Effective 只是一个 bit。如果设置为开启,那么在执行 execve 函数后,Permitted 集合中新增的 capabilities 会自动出现在进程的 Effective 集合中。
3.4 execve() 权能变化规则
execve()执行二进制文件,产生新进程。新进程的capability如下:
3.5 capability的实现
- 在进程的task_struct结构体中设置capability,capability取值需要计算。
- 在进程进行特权操作时,检查进程是否具有相应的capability,而不是判断是否为root用户
4. 安卓中 的 capabilities
(1)在 Android 4.3 之前,app进程 可以直接借助 有SUID标志位的su二进制文件 来获取root用户权限。
(2)Android 4.3之后,app进程基于SUID获取root权限的方案被禁用。主要措施是:
- /system 和 /data 分区以 nosuid option被挂载,让文件的SUID标识失效。
- app进程是由zygote 进程 fork产生的。zygote进程设置了NO_NEW_PRIVS标志,父进程的NO_NEW_PRIVS位会在父进程fork、clone和execve时,被子进程继承 ,并且不能被撤销。NO_NEW_PRIVS标志会让当前进程在执行可执行文件时,进程的EUID和EGID不受可执行文件的SUID和SGID位影响。
关于zygote进程fork出app进程的过程
参考:https://www.toutiao.com/article/6777894692462789124/
Android 4.3之后,/system 分区也被以 nosuid option被挂载;
那么,系统进程如何获取root权限呢?
app进程是否可以使用同样的方式呢?
(3)系统进程改用 Capability来获取root权限,但app进程不行。
-
系统daemon可通过可执行文件的capability来获取进程的cap_effective,
-
但app进程不能这样做,因为app进程是由zygote 进程fork出来的;而zygote进程设置了NO_NEW_PRIVS标志,使得app进程无法通过可执行文件的capability来获取cap_effective。
P’(ambient) = (file is privileged) ? 0 : P(ambient)
P’(permitted) = (P(inheritable) & F(inheritable)) | (F(permitted) & P(bounding))) | P’(ambient)
P’(effective) = F(effective) ? P’(permitted) : P’(ambient)
也就是:
P’(effective) = F(effective) ? ( (P(inheritable) & F(inheritable)) | (F(permitted) & P(bounding))) | ( (file is privileged) ? 0 : P(ambient) ) ) : ( (file is privileged) ? 0 : P(ambient) )NO_NEW_PRIVS标志会使SUID和SGID位无法改变进程的 uid 或 gid,file capability也不会被添加到进程的capability中。也就是NO_NEW_PRIVS标志会使获取root权限的 SUID方案和file capability方案 失效。
参考:https://www.kernel.org/doc/html/latest/translations/zh_CN/userspace-api/no_new_privs.html
(4)对app进程切换EUID的其他限制:
- 仅当进程的cap_bset数组中有 SETUID/SETGID capability时,进程才能切换 UID。而从Android 4.4开始,zygote fork app进程时,会对所有fork出来的子进程进行CAPBSET_DROP动作,让子进程不具有任何capability。
- 从Android Oreo开始,OS通过SECCOMP过滤器阻止某些SYSCALL,app进程更改UID/GID的能力被进一步抑制。
(5)SELinux带来的约束
即使一个进程的euid变成了0,或者拥有了所有capability, 它也必须受到SELinux策略的约束。
也就是,SELinux机制进一步约束了root权限进程的行为。
参考:https://forum.xda-developers.com/t/info-is-it-possible-to-install-windows-ios-or-linux-on-android-device.3763961/post-77437874
(#6)
《Linux Capabilities 简介》
https://www.cnblogs.com/sparkdev/p/11417781.html
《Linux Capabilities 入门教程:概念篇》
https://icloudnative.io/posts/linux-capabilities-why-they-exist-and-how-they-work/
很好的介绍capability的课件:
《Linux中的SetUid和capability权能机制》
https://zhuanlan.zhihu.com/p/484164795