本文共两部分,各位师傅耐心食用。

第一部分翻译自 SHADOW SUID FOR PRIVILEGE PERSISTENCE: PART 1

Part One

影子SUID 原文为Shadow SUID 看过张艺谋的影视作品影子的都可以轻易理解这个概念,和电影中邓超扮演的角色一样。

0x01 前言

SentinelOne Linux代理目前多了的一个新特性,也就是Shadow SUID保护。本文将分为两部分,解释这个特性是如何产生的,以及为什么需要它。

大多数情况下,在Linux中获得root权限并不容易。其安全更新是按照每天潍州起发布的,当一个新的0day或漏洞在野外出现后,几个小时内就会被修复。这种便捷多亏了打包分发系统(又名“存储库”),补丁程序也能相对快速地找到到达漏洞点的方法。因此,攻击者一旦在目标上取得相关权限,他首先要做的就是维护这些权限。最常见方法之一是利用setuid(即suid)机制。

0x02 什么是SUID?

Linux有三个最基本权限,即读、写和执行。除此之外,Linux还有一些用于特定情况的权限。比如setuidsetgid,得程序再特定的用户或组(通常是root用户)下运行时才能使用,但实际的情况是我们并不希望给予程序的用户更高的权限。当执行带有setuid/setgid命令的程序时,程序可以要求操作系统给予进程所有者(或组)的权限。

ping命令为例。为了发送ICMP包,ping需要使用原始套接口,这在Linux中就需要root权限了。然而ping只是经常使用的一个基本命令之一,我们不能允许执行ping这样基础命令的用户都拥有更高的权限。那么如何解决这个问题? 没错,就是SUID权限。当ping被执行时,无论哪个用户执行它,它实际上都作为root用户来运行。

作为攻击者,一旦取得了更高的权限,就可以轻松地编写一个只需要打开shell就能获取suid权限的程序。这样做之后,我们可以从低权限的webshell或其他后门进入机器,然后删除的suid-shell重新获得root权限。

值得庆幸的是suid shell非常容易定位到。由于在系统上有很多这样的可执行文件存在风险,所以大多数Linux发行版附带的suid二进制文件贼少。管理员可能会定期在机器上查找这些二进制文件,并且找到攻击者创建的新suid二进制文件。攻击者对此常见的解决方案是利用恶意的suid二进制文件替换原先自带的合法的suid二进制文件;但是,这些文件通常都是是受到密切监视的,有很多方法可以保护suid文件不被篡改。甚至可以为此使用内置的dpkg --verifyrpm -Va命令来验证。

0x03 什么是影子SUID?

影子SUID与普通SUID文件相同,只是它没有setuid位,这使得查找或注意非常困难。

影子SUID的工作方式是利用binfmt_misc机制(Linux内核的一部分)继承现有setuid二进制文件中的setuid位。

继承在执行目标setuid二进制文件时生效,该二进制文件是在执行之前为此目的设置的。需要注意这里并不需要修改原始suid文件来指向它。生成影子SUID的先决条件是机器上至少有一个已存在的suid二进制文件,细节参照本文第二部分

Demo:

https://www.youtube.com/embed/R6BQz8aywBs

0x04 鸡肋与否?

因为自2004年以来,它一直是Linux内核的一部分,所以大概率的可能是可以在目标机器上创建一个影子SUID。可通过执行以下命令来检查内核是否附带binfmt模块:

grep 'BINFMT_MISC' /boot/config-`uname -r`

0x05 如何自检?

首先检查binfmt_misc文件系统是否被使用:

mount | grep binfmt_misc

然后检查相关的路径(最常见的如下):

ls -la /proc/sys/fs/binfmt_misc

结果应该是这样的:

registerstatus外的文件都应仔细检查以下。

如果是这样:

打印出这些额外的文件,看看“flags”字段中是否有“C”,如下图所示:

如果需要删除binfmt_misc规则,只需在文件中写入“-1”即可。

值得注入的是它也可能是一个合法文件,所以如果不确定是否为恶意文件前请看看其他文件以确定其合法性。

0x06 如何清除?

没有额外的保护的情况下,最好的简单解决办法即删除所有相关的.ko文件(有一定风险 需谨慎之):

$ modinfo -n binfmt_misc
/lib/modules/4.10.0-42-generic/kernel/fs/binfmt_misc.ko

$ rm $(modinfo -n binfmt_misc)

Part Two

第二部分翻译自SHADOW SUID FOR PRIVILEGE PERSISTENCE: PART 2

0x01 前言

上集说到setuid是什么,以及影子SUID如何获取相同的权限持久化功能。本文将深入研究权限持久化是如何工作的以及它的技术细节。

0x02 命令是如何被执行的?

在理解其是如何执行的之前,我们首先需要了解在Linux上执行命令时实际发生了什么。

当执行execve()这个系统调用时,内核会读取文件的前128个字符。然后遍历注册的二进制格式的处理程序,以确定应该使用哪个处理程序。这样,当我们执行一个以#!开头的文件时内核就会将其识别为一个脚本,并使用binfmt_script处理程序来查找相关的解释器(如shebang之后所示)。同理当文件以\x7fELF开头的文件时,内核会认为这是一个Linux二进制文件,并使用binfmt_elf处理程序将二进制文件加载到elf解释器中取执行。

0x03 未知文件的处理

在早期的Linux版本中,当你想要添加一个新的文件类型时,必须编写一个新的内核模块去注册一个新的处理程序,并将其指向相关的解释器来执行该文件。自从2.1.43(1997年6月)后的版本以来,binfmt_misc这个模块被引入使用,其允许添加新的文件类型,使得整个过程变得简单多了。如binfmt_misc的说明文档所诉:

该内核功能允许您在终端Shell中直接输入其名称来调用几乎所有程序(限制见下文)。 包括但不限于JavaPythonEmacs

实现这一点的前提是得告诉binfmt_misc用哪个二进制文件调用哪个解释器。 Binfmt_misc通过将文件开头的某些字节来匹配并识别二进制文件的类型。 Binfmt_misc还可以识别如.com.exe等文件名扩展名。

导致这个问题的“特殊标志”直到2004年8月才出现在Linux 2.6.8的更新日志中。

使用binfmt_misc提升权限并执行不受信任的代码。 有的人会觉得这是假的吧 所有binfmt_misc的解释器都是可信的,因为只有root权限才能注册使用它们。 但这个缺陷是对binfmt_misc(和binfmt_script)的传统认知的绝对冲击。

这也就意味着维护人员从这个点上开始认可这个问题,并把他当做一个漏洞来看看待了; 然而也正如LKML中的进一步讨论所示,这个问题待修复但是并不算很严重。

谨慎使用此功能,因为解析器在运行root所拥有的setuid二进制文件时以及对其赋予了root权限...。只有root可以使用binfmt_misc注册解释器。 该功能缺陷已记录在案,建议运维管理员们小心处理。

0x04 小试牛刀

先看一下它的工作原理。

新建一个没有shebang标头的Python脚本:

尝试在终端不指明解释器直接运行以下:

如图,报错了。 因为没有告诉系统用哪个程序负责解释脚本。 默认情况下是bin/sh用作解释器,解释器不对口,所以除了错误,正确的运行姿势是python script.py

.py文件添加一个指定的解释器:

现在再去执行就能够轻松通过啦:

要了解如何如何利用这个缺陷去触发漏洞,还需要返回到文档中查看C - credentials标记的部分:

目前binfmt_misc的行为是根据解释器来计算新进程的凭证和安全令牌。 包含此标志时,将会根据二进制文件计算这些属性。应谨慎使用此功能,因为当root用户拥有的setuid二进制文件与binfmt_misc一起运行时,解释器就会以root权限来运行代码。

0x05 场景假设

新建一个python脚本,使其获得root权限并并赋予setuid权限。

看一下执行的结果

即使Python解释器不是setuid,也能够强制让它成为setuid,只是因为解释的文件具有setuid权限而已。

如此一来可以可以试试能否使用本地setuid二进制文件之一作为“script”并执行我们自己的特制的文件以作为解释器。

首先找到一个傀儡suid程序,它将是我们的“解释文件”,它在系统中的所有可执行文件中需具有唯一的签名; 否则可能会被非目标程序执行,甚至可能不是suid的。 因此必须确保在文件的前128个字节中拥有自己的唯一签名。

写了一个小脚本来查找所有unique-header setuids

https://github.com/Sentinel-One/shadowsuid/blob/master/locate_unique_suids.py

遍历出来后随机选一个进行测试,这里就选ping吧,其他suid文件都可以!

首先提取其文件的前128个字节,并使用它们来创建新的binfmt_misc

如果参数有问题,执行时就会报错; 无报错则说明一切正常。可以检查一下/proc/sys/fs/binfmt_misc/.ping来验证注册的规则。

仔细看您可能还会发现我在开头使用了名称.ping并带有一个点。在Linux中,前缀为点的文件是隐藏的,并且ls命令看不到。 用于隐藏我们刚刚创建的binfmt_misc规则文件是很有用的。

不带参数的执行ls结果如下:

自动化脚本:

https://github.com/Sentinel-One/shadowsuid/blob/master/shadow_suid.py

执行完脚本再运行ping你会发现:

正如图所示,已经以非ROOT用户的身份执行了ping命令,并且我们自己的非suid程序是以root身份执行,就好像它本身就是suid一样!

0x06 防御问题

有趣的是这个漏洞带有自己的内置保护:很难被检测到。如果删除解释器再尝试使用ping时会报以下错误:

虽然上述错误的原因是缺少解释器,但系统会将其全部归咎于setuid文件。大多数用户可能会在找出真正的原因之前都会选择直接重新安装系统。

0x07 其他问题

这里你可能会想,ping被替换后如果管理员想执行ping命令时怎么办,这里原先我以为很容易,在我们的脚本里面调用原来的ping就好了,实际的情况是会陷入死循环,根本无法调用。

提供两种解决方案。一是可以使用ld.so执行原始程序,其允许您使用elf解释器执行程序。这种方法中比较简单的,但我并不喜欢它,因为你需要做很多清理工作(作为攻击者而言 这是很不愿意的)。这个方法意味着ld.so成为第一个参数,当管理员通过ps命令检查发生了什么时就会被发现。

以一种方法是将0写入规则文件,再次执行原始命令时通过将1写入规则文件来重新启用前面被禁用的规则。

exp代码:

https://github.com/Sentinel-One/shadowsuid/blob/master/interpreter_final.c

如此一来便解决了第一张方法的缺陷。

0x08 总结

从现在的互联网的威胁情况来看,这一内核功能的变化似乎是一个边缘案例的安全问题,结果事实证明这是一个严重的漏洞。常见的运维工具难以检测到影子suid的存在,这个漏洞几乎影响到使用Linux系统的每一个人!

点击收藏 | 3 关注 | 1
登录 后跟帖