Anthony Lineberry 论 /dev/mem Rootkit

作者:Mick Bauer

在 2009 年 4 月中旬的欧洲黑帽大会上,Anthony Lineberry 发表了一篇有趣的论文,讲述了拥有 root 权限的攻击者如何使用 /dev/mem rootkit,通过直接修改内核内存来隐藏他们的攻击。虽然这不是一个全新的技术,但 Anthony 在 BHE 上的演讲使其重新成为焦点。此外,Lineberry 还描述了他正在开发的 proof-of-concept 工具,以演示如何在现实世界中利用这项技术。

一方面,一旦攻击者获得了您系统上的 root 权限,游戏就结束了——攻击者拥有完全控制权,您进一步防御和缓解的所有希望都消失了。从这个角度来看,攻击者直接写入内核内存的能力与他们作为 root 用户可以做的其他事情相比,并没有太大的不同,甚至更糟。

但是,另一方面,即使您的系统遭受 root 权限泄露,您仍然希望机会至少检测到这种泄露,以便采取措施。因为 rootkit 的目的就是阻止这种情况发生,所以您有必要采取一切可能的预防措施来防范它们。因此,从这个意义上说,新的 rootkit 技术实际上非常值得我们关注和重视。

在本文中,我将提供一些关于 rootkit 和 /dev/mem 的背景知识,Anthony Lineberry 将以我们最近的一次对话的形式,进一步阐明 /dev/mem rootkit。

Rootkit 回顾

那么,rootkit 到底是什么?简而言之,rootkit 是一种恶意代码,它会隐藏或歪曲系统状态,使其呈现给系统管理员的状态与真实状态不符。

术语中的“kit(工具包)”部分反映了早期 UNIX rootkit 的形式,它们是系统命令的一对一替换集合,例如 ls 和 ps。替换后的命令在很大程度上表现得像它们替换的命令,只是它们具有选择性盲区。例如,rootkit 的 ls 命令可能会在它显示的文件列表中省略攻击者的目录 /...my_evil_tools,而 rootkit 的 ps 命令可能会在进程列表中省略攻击者的程序 erase_recent_logs。换句话说,rootkit 旨在隐藏系统攻击者在目标系统上立足后的活动。

第一代 rootkit 的一个问题是,它们的功能仅限于 rootkit 版本替换的那些特定命令。如果系统管理员使用一些命令或实用程序而不是 ls 来查看包含攻击证据的目录的内容,该怎么办?

另一个问题是可检测性。如果系统受到系统完整性软件(如 Tripwire)的保护,该软件会检测并报告对系统文件的未经授权的更改,那么在不被检测到的情况下替换系统命令可能会很困难。

这两个问题在很大程度上通过可加载内核模块 (LKM) rootkit 的出现而“解决”。顾名思义,LKM rootkit 由攻击者加载的一个或多个内核模块组成。LKM rootkit 重新映射系统实用程序访问的实际系统调用(也称为内核符号),使系统命令本身保持不变。毋庸置疑,这是一种非常强大的技术。

尽管 LKM rootkit 仍然非常强大,但它们仍然可以被检测到,例如,通过将内核的系统映射(一个显示所有受支持的系统调用的正确内存地址的文件)与内存中的实际系统调用地址进行比较。在没有 LKM 感染的系统上,这些地址应该与系统映射中的地址相同。

/dev/mem 和 /dev/kmem

那么,这就是 rootkit 运行的问题空间——以一种本身不引人注目的方式隐藏攻击活动和结果。但是,/dev/mem 是什么?这个特定的内核接口与 LKM 有什么不同?

/dev/mem 是一个字符设备,它为用户空间(即内核或内核模块以外的程序)中具有 root 权限的进程提供对物理内存的直接访问。/dev/kmem 与此相同,但它使用“虚拟”内存地址(如内核使用的地址)而不是物理内存的“原始”地址。与 /proc/kcore(它为开发人员和内核黑客提供类似的功能)不同,/dev/mem 和 /dev/kmem 不仅授予读取访问权限,还授予写入内存的权限。

您可能会理所当然地认为,像 /dev/eth0、/dev/hda 和 /dev 中的其他特殊文件一样,/dev/mem 是用户空间应用程序与内核通信所需的基本接口。但事实并非如此。除了内核开发人员之外,历史上 /dev/mem 的另一个主要用户是 X Window 系统,它的部分组件仍然使用 /dev/mem 来访问视频适配器的内存和控制寄存器。

至少在 /dev/kmem 的情况下,有些人认为这些特定设备对攻击者的用处大于对合法用途的用处。早在 2005 年,lwn.net 的 Jonathan Corbet 就说过,“有人建议 rootkit 是此类访问的最大用户社区”(有关完整上下文,请参阅“资源”;他特别指的是 /dev/kmem)。

希望我没有夸大其词,因为我既不是内核开发人员,也不是 X Windows 系统专家,我不会自作主张地主张废除 /dev/mem 或 /dev/kmem 本身。相反,我试图将所有这些都放在一个有用的上下文中——这使我们想到了 Anthony Lineberry。

访谈

Anthony Lineberry 是一位安全软件工程师和 Linux 安全研究员。Silvio Cesare 在 1998 年和 devik 在 2001 年的 Phrack 杂志上首次撰写了关于使用 /dev/kmem 对 Linux 系统进行 rootkit 攻击的概念。除了将这种很少被讨论的攻击向量重新带回人们的视线之外,Anthony Lineberry 还发现了一些欺骗内核为注入代码分配内存的新方法。在 Anthony 的欧洲黑帽大会演讲之前和之后,我立即通过电子邮件与他进行了交谈。

MB: 你好,Anthony。感谢您抽出时间与 Linux Journal 对话!看起来这种攻击的后果与可加载内核模块 rootkit 的后果非常相似。显然,这里不是详细论述的最佳论坛,但您能否为我们的读者描述一下您的 /dev/mem 攻击?

AL: 我们本质上是使用 mem 设备将代码直接注入到内核中。/dev/mem 只是一个字符设备,它提供了对物理可寻址内存的接口。寻址到一个偏移量并执行读取将从内存中的该物理位置读取。将内核中的虚拟地址转换为它们映射到的物理地址,您可以使用简单的读写操作来热补丁代码,直接注入到内核中。使用各种启发式方法,您可以找到内核中的各种重要结构并对其进行操作。在这一点上,您能够控制行为并操作内核内部的几乎所有内容,包括系统调用表、进程列表、网络 I/O 等等。

MB: 攻击者是否必须是 root 用户才能在内存中定位和操作这些结构?

AL: 是的,您肯定必须是 root 用户才能读取/写入此设备并操作内核内部的任何结构。

MB: 这与 LKM rootkit 有什么不同?

AL: 总的来说,LKM 在加载到内核时会产生很多“噪音”。使用这些技术,我们避免了所有这些,因为我们是直接注入到物理内存中。使用 LKM 确实使开发 rootkit 容易得多。所有的精力都可以投入到实际代码中,而不必可靠地确定内核内部的所有内容的位置。虽然我们可以读取/解析内核内存中的导出表来定位几乎所有导出的符号。

针对 Linux 内核 rootkit 的通用建议缓解方法是配置一个非模块化内核,并将所有正在使用的设备编译到内核中。在这种情况下,我们仍然能够将代码注入到内核空间。

MB: 您是否在虚拟化环境中测试过这种攻击?虚拟化内存的行为是否有所不同?

AL: 是的,这些方法在虚拟化环境中也有效。我遇到的主要区别是一些由虚拟机监控程序处理的特殊指令的行为有所不同。特别是在这种情况下,我用于在内存中定位 IDT/系统调用表的 lidt 指令会返回一个伪造的虚拟地址,但这些问题大多很容易克服。

MB: 防御 /dev/mem 攻击的最佳方法是什么?

AL: 最好的防御方法是在内核中启用 CONFIG_STRICT_DEVMEM(最初在 2.6.26 中称为 CONFIG_NONPROMISC_DEVMEM),这将 mem 设备上的所有操作限制为物理内存的前 256 页(1MB)。这种限制将允许 X 和 DOSEMU 等合法使用此设备的程序仍然正常运行,但阻止其他人读取这些低内存区域之外的内存。不幸的是,默认配置禁用了此保护。

MB: 您是否联系过任何主要的 Linux 发行商(Red Hat、Novell 等)?他们中是否有人承诺在其默认内核中启用此设置?

AL: 没有,[虽然] 许多主要发行版在其发布版本中默认启用了此设置。我想计划编译一个列表,列出谁启用/不启用此设置。

关于缓解的一些说明

正如 Anthony 所说,除非从您的内核中完全移除 /dev/mem 和 /dev/kmem(这几乎肯定会破坏某些东西,尤其是在 X Window 系统中),否则最好的防御方法是在您的内核中编译 CONFIG_STRICT_DEVMEM=y。Fedora 和 Ubuntu 系统的默认内核已经编译了此选项。RHEL 更进一步,通过使用 SELinux 策略来限制对 /dev/mem 的访问。

如果您不知道您系统的内核是否使用 CONFIG_STRICT_DEVMEM=y 编译,有几种不同的方法可以查明。根据您的 Linux 发行版,您系统正在运行的内核的配置文件可能存储在 /boot 中,名称类似于 config-2.6.28-11-generic。如果是这样,您可以 grep 该文件以查找 DEVMEM。如果不是,您的内核可能以名为 /proc/config.gz 的文件的形式保存了其配置副本,在这种情况下,您可以使用命令

zcat /proc/config.gz | grep DEVMEM

否则,您需要获取正在运行的内核的源代码,执行make oldconfig(实际上会提取您正在运行的内核的配置),并检查生成的 .config 文件。在任何这些情况下,如果 CONFIG_STRICT_DEVMEM 设置为 n 而不是 y,您都需要编译自定义内核。

要做到这一点,在完成make oldconfig之后,即使您已经知道您的内核缺少 CONFIG_STRICT_DEVMEM 启用,这也是一个好主意,因为您可能只对更改 CONFIG_STRICT_DEVMEM 而保持内核的其余部分相同感兴趣,您可以执行以下操作之一make menuconfigmake xconfig。在出现的菜单中,选择 kernel hacking(内核 hacking),查找选项 Filter access to /dev/mem(过滤对 /dev/mem 的访问),将其设置为 y,退出,保存您的配置,然后重新编译。

如果整个内核编译过程对您来说是新的,请参阅您的 Linux 发行版的官方文档以获取更详细的说明。编译自定义内核的过程现在非常特定于发行版,特别是如果您想生成自定义 RPM 或 deb 包(这实际上是在基于 RPM 和 deb 包的系统上安装自定义内核的最不具破坏性的方法)。

资源

Anthony Lineberry 的“通过 /dev/mem 进行恶意代码注入”:dtors.org/papers/malicious-code-injection-via-dev-mem.pdf

Anthony Lineberry 在 2009 年欧洲黑帽大会上的演讲幻灯片“内核领域的爱丽丝:通过 /dev/mem 进行恶意代码注入”:dtors.org/papers/injection-via-dev-mem.pdf

Silvio Cesare 的“运行时内核 kmem 补丁”:doc.bughunter.net/rootkit-backdoor/kmem-patching.html

sd 和 devik 的“无需 LKM 的 Linux 即时内核补丁”,Phrack 58(2001 年 12 月 28 日):www.trust-us.ch/phrack/show.php@p=58&a=7

Rainer Wichmann 的“Linux 内核 Rootkit”:www.la-samhna.de/library/rootkits/index.html

Jonathan Corbet 的“谁需要 /dev/kmem?”:lwn.net/Articles/147901

Jonathan Corbet 的“通过 /dev/mem 加载 rootkit 的详细信息”:lwn.net/Articles/328695

Mick Bauer (darth.elmo@wiremonkeys.org) 是美国最大的银行之一的网络安全架构师。他是 O'Reilly 图书 Linux 服务器安全,第二版(以前称为 使用 Linux 构建安全服务器)的作者,偶尔在信息安全会议上发表演讲,也是“网络工程波尔卡”的作曲家。

加载 Disqus 评论