由于开源生态系统被广泛采用,它已成为供应链攻击的主要目标。恶意行为者经常利用开源软件包的内置功能自动分发和执行有害代码。他们特别喜欢两种技术:在软件包安装时执行的自动预安装脚本,以及导入恶意依赖项的看似无辜的软件包。
随着这些策略越来越为人所知,当前的安全工具和警惕的开发人员已经能够快速检测它们。然而,一个经常被忽视但潜在危险的功能仍然存在:入口点。
这篇博文探讨了攻击者如何利用多个编程生态系统中的入口点(重点是 Pypi)诱骗受害者运行恶意代码。虽然这种方法无法像自动脚本或恶意依赖项那样立即破坏系统,但它为耐心的攻击者提供了一种更微妙的方法来渗透系统,从而有可能规避标准安全措施。
通过了解这个鲜为人知的载体,我们可以更好地防御不断演变的开源供应链攻击。
关键点
入口点是公开包功能的强大功能,容易受到各种生态系统的利用,包括 PyPI(Python)、npm(JavaScript)、Ruby Gems、NuGet(.NET)、Dart Pub 和 Rust Crates。
攻击者可以利用这些入口点在运行特定命令时执行恶意代码,从而对开源领域造成广泛的风险。
攻击方法包括命令劫持(冒充流行的第三方工具和系统命令)以及通过恶意插件和扩展针对开发过程的各个阶段。每种方法都有不同程度的潜在成功率和检测风险。
入口点攻击虽然需要用户交互,但却为攻击者提供了一种更隐蔽、更持久的入侵系统的方法,有可能绕过传统的安全检查。
这种攻击媒介对个人开发者和企业都构成了风险,凸显了对更全面的 Python 包安全措施的需求。
了解 Python 入口点
入口点是打包系统的一个强大功能,它允许开发人员将特定功能公开为 cli 命令,而无需用户知道包的确切导入路径或结构。
入口点有多种用途,包括:
-
创建用户安装包后可以运行的命令行脚本。
-
定义插件系统,第三方包可以扩展核心包的功能。
最流行的入口点是console_scripts,它指向一个函数,该函数作为命令行工具提供给安装你的软件包的任何人。
虽然入口点主要用于增强模块化和插件系统,但如果使用不当,就会成为恶意行为者嵌入和执行有害代码的载体。要了解攻击者如何利用 Python 入口点为自己谋利,我们首先要了解入口点最初的工作方式。
如何在包元数据中定义入口点
入口点定义的位置和格式可能因包格式(wheel 或源分发)而异。
源分布(.tar.gz)
对于源代码发行版,入口点通常在包的设置配置中定义。对于传统设置,入口点可以在setup.py、setup.cfg中定义,对于更现代的打包方法,入口点可以在pyproject.toml中定义。
以下是如何在 setup.py 中定义入口点的示例:
wheel 文件 (.whl)
在 wheel 文件(一种构建的包格式)中,入口点在.dist-info目录内的entry_points.txt文件中定义。
上述示例中的entry_points.txt文件可能如下所示:
入口点的语法遵循以下模式:
-
name:入口点的名称(例如,控制台脚本的命令名称)
-
package.module:Python 模块路径
-
object:要使用的模块内的对象(函数、类等)
在上面的例子中,my_command是一个将在安装期间创建的控制台脚本。在软件包安装完成后的任何时候,当用户在终端中输入my_command时,它将执行mypackage.module中的my_function。
plugin_name是一个自定义入口点,可供my_package用来发现插件。它指向my_package.plugins中的PluginClass。
当安装包时,这些入口点会记录在包的元数据中。其他包或工具可以查询此元数据来发现和使用定义的入口点。
如果攻击者可以操纵合法软件包的元数据或诱使用户安装恶意软件,那么只要调用定义的命令或插件,他们就有可能在用户的系统上执行任意代码。在下一节中,我将提供攻击者可以用来诱骗某人通过入口点执行恶意代码的多种方法。
了解操作系统中的 CLI 命令
命令行界面 (CLI) 命令是用户通过基于文本的界面与操作系统交互的主要方式。这些命令由 shell 解释和执行,shell 充当用户和操作系统之间的中介。
当用户输入命令时,shell 会遵循特定的解析机制来定位并执行相应的程序。不同的 shell 之间的确切顺序可能略有不同。但是,该过程通常首先按顺序检查 PATH 环境变量中列出的目录,然后运行找到的第一个匹配的可执行文件。用户可以通过在终端中输入命令“echo $PATH”来查看其当前 PATH (确切的命令因操作系统而异),该命令会显示 shell 搜索可执行文件的目录列表。
此解析过程可确保当用户输入命令时,系统会采取适当的操作。在考虑 Python 入口点(可以创建新的 CLI 命令)如何与现有系统命令交互或潜在干扰时,了解此过程至关重要。
Ubuntu 系统上的终端输出显示“ls”命令执行、使用“which ls”的 PATH 位置以及系统的 PATH 环境变量,显示“ls”PATH 优先级。
攻击者如何滥用入口点来执行恶意代码
恶意攻击者可以通过多种方式利用 Python 入口点诱骗用户执行有害代码。我们将探讨多种策略,包括命令劫持、恶意插件和恶意扩展。
命令劫持
模仿流行的第三方命令
恶意软件可以使用入口点伪装成广泛使用的第三方工具。这种策略对于在工作流程中频繁使用这些工具的开发人员尤其有效。
例如,攻击者可能会创建一个带有恶意“aws”入口点的包。当经常使用 AWS 服务的毫无戒心的开发人员安装此包并随后执行 aws 命令时,伪造的“aws”命令可能会泄露他们的 AWS 访问密钥和机密。这种攻击在 CI/CD 环境中可能具有毁灭性,因为 AWS 凭证通常存储在 CI/CD 环境中以用于自动部署 — 可能让攻击者访问整个云基础设施。
另一个例子是恶意软件冒充“docker”命令,针对使用容器化应用程序的开发人员。伪造的“docker”命令可能会在构建或部署期间秘密将图像或容器规范发送到攻击者的服务器。在微服务架构中,这可能会暴露敏感的服务配置,甚至导致专有容器图像的泄露。
其他可能成为模仿目标的流行第三方命令包括但不限于:
-
npm(Node.js 包管理器)
-
pip(Python 包安装程序)
-
git(版本控制系统)
-
kubectl(Kubernetes 命令行工具)
-
terraform(基础设施即代码工具)
-
gcloud(Google Cloud 命令行界面)
-
heroku(Heroku 命令行界面)
-
dotnet(.NET Core 的命令行界面)
这些命令中的每一个都被广泛应用于各种开发环境中,这使得它们成为寻求最大化其恶意软件影响的攻击者的有吸引力的目标。
模拟系统命令
通过使用常见的系统命令名称作为入口点,攻击者可以冒充基本的系统实用程序。“touch”、“curl”、“cd”、“ls”和“mkdir”等命令都可能被劫持,当用户尝试使用这些基本工具时,会导致严重的安全漏洞。
虽然这种方法可能为受害者意外执行恶意代码提供了最大的机会,但它也为攻击者带来了最大的失败风险。这种方法的成功主要取决于PATH顺序。如果包含恶意入口点的目录在 PATH 中出现的时间早于系统目录,则将执行恶意命令而不是系统命令。这更有可能发生在优先考虑本地包目录的开发环境中。
要记住的另一件事是,全局安装的软件包(需要 root/admin 权限)可能会覆盖所有用户的系统命令,而用户安装的软件包只会影响特定用户的环境。
安装恶意软件包前后 Ubuntu 终端输出的比较。PATH /home/ubuntu/.local/bin/ls 中添加了“ls”命令,该命令的优先级高于合法 ls 命令的 PATH。
使用命令包装增强攻击
在这些命令劫持策略中,虽然攻击者只需覆盖 CLI 命令即可,但未被发现的可能性非常低。一旦受害者无法执行命令,他们很可能会立即产生怀疑。但是,通过一种称为“命令包装”的技术,这些攻击可以变得更加有效和隐蔽。包装不是简单地替换命令,而是创建一个入口点,作为原始命令的包装器。它的工作原理如下:
-
当用户调用命令(无论是模仿的第三方工具还是尝试模仿系统命令)时,就会触发恶意入口点。
-
除了悄悄执行攻击者的恶意代码之外,它还会使用所有用户的参数调用原始的合法命令。
-
最后,它将合法命令的输出和退出代码返回给用户。
这种命令包装方法特别危险,因为它会在用户不知情的情况下执行恶意代码,同时保持正常运行的表象。由于合法命令仍在运行,其输出和行为也得以保留,因此没有立即受到攻击的迹象,因此通过正常使用很难检测到攻击。这种隐秘的方法允许攻击者保持长期访问权限,并可能在不引起怀疑的情况下泄露敏感信息。
然而,实施命令包装需要攻击者进行额外的研究。他们需要了解不同操作系统上目标命令的正确路径,并考虑其代码中的潜在错误。这种复杂性随着攻击目标系统的多样性而增加。
另一种方法是,根据被劫持的命令,恶意软件不仅执行其隐蔽操作,还复制原始命令的部分或全部功能。包装器不会调用真实命令,而是模拟其行为。这种方法可以进一步降低怀疑程度,尤其是对于较简单的命令,但攻击者需要付出更多努力才能在各种情况下准确模仿原始命令的行为。
这些攻击的成功最终取决于所安装的恶意软件包及其脚本目录在系统 PATH 中的优先级。
恶意插件和扩展
另一种滥用入口点的强大技术是通过为流行的 Python 工具和框架创建恶意插件。这种方法尤其危险,因为它针对的是开发和测试过程本身。
操作 pytest
举个例子,让我们考虑一下攻击者如何针对Python 生态系统中广泛使用的测试框架pytest进行攻击。通过创建恶意的 pytest 插件,攻击者可能会破坏整个测试过程的完整性。
此类攻击的具体运作方式如下:
-
攻击者创建一个插件,使用 pytest 的入口点系统注入恶意代码。
-
这个插件作为一个看似有用的测试实用程序进行分发。
-
一旦安装,该插件可以操纵测试过程的各个方面,例如断言处理。
然后,恶意插件可以在测试期间在后台秘密运行恶意代码。恶意插件还可以覆盖 pytest 的断言比较,例如,导致所有相等性检查都通过,而不管其实际值如何,从而导致测试结果出现误报,让有缺陷或易受攻击的代码在不被注意的情况下通过质量检查。
在下面的视频演示中,我们展示了这种恶意插件如何针对 pytest 的断言处理,从而允许攻击者操纵测试结果而不让开发人员感到意外。在此示例中,开发人员正在尝试对基本计算器包进行简单的测试扫描。
操作Flake8
攻击者还可以针对流行的开发工具,操纵它们来运行恶意扩展。Flake8是 Python 生态系统中广泛使用的 linting 工具,就是这样一个例子。由于Flake8使用入口点来发现和加载扩展,因此它成为恶意行为者的潜在目标。
攻击者可能会通过创建伪装成有用的 linting 规则的恶意扩展来利用 Flake8。此扩展将被定义为包的设置配置中的入口点。例如,设置文件可能会指定一个名为“MCH”的入口点,指向包内的恶意检查器类。
恶意检查器的实现可能包括对受害者系统执行有害操作、向代码中注入恶意“修复程序”或操纵 linting 结果以隐藏或创建问题的功能。当用户在其代码库上运行 Flake8 时,此恶意扩展将激活,允许攻击者执行其有害代码。
这种攻击尤其危险,因为 linting 工具通常在整个代码库上运行,使攻击者能够广泛访问源代码。此外,这种攻击可以通过看似有用的 linting 规则进行,因此不太可能引起怀疑。它可以作为更大规模供应链攻击的一部分,以收集情报或将漏洞引入目标的代码库。
解决 .whl 文件限制
Python wheel(.whl文件)因其在软件包安装中的性能优势而变得越来越流行。然而,它们对攻击者来说是一个独特的挑战
虽然.tar.gz和.whl文件都可能包含setup.py文件,但.whl文件在安装过程中不会执行setup.py。这一特性传统上使攻击者在使用.whl文件时更难以在安装过程中实现任意代码执行。
但是,我们讨论的入口点攻击方法为这一限制提供了一种解决方法。通过操纵入口点,攻击者可以确保在运行特定命令时执行其代码,即使包以.whl文件的形式分发。这一点尤其重要,因为当开发人员使用“pip -m build”等命令构建 Python 包时,较新的 pip 版本会自动创建.tar.gz和.whl文件。此外,pip会在安装期间优先将.whl文件交付给用户
软件包格式和安装行为的这种转变给攻击者带来了新的机会。许多安全工具专注于分析安装过程中预安装脚本的执行情况,这些脚本通常与.tar.gz文件相关联。因此,它们可能会错过以.whl文件形式分发的软件包中的恶意代码,尤其是当恶意行为是通过入口点而不是立即执行触发时。
其他生态系统的切入点
虽然本博客主要关注 Python,但利用入口点进行恶意攻击的情况并不只限于 Python 生态系统。通过研究,我们确认这种类型的攻击媒介存在于其他几个主要生态系统中,包括:
npm(JavaScript)、Ruby Gems、NuGet(.NET)、Dart Pub 和 Rust Crates,但漏洞可能不仅限于这些。
了解入口点如何在各种编程语言和包管理器中发挥作用,对于掌握这种潜在安全风险的普遍性和制定全面的防御策略至关重要。
结论
入口点虽然对于合法的软件包开发来说是一个强大而有用的功能,但也可以被操纵以在多个编程生态系统中传播恶意代码
攻击者可以通过各种方法利用此机制,包括命令劫持和为流行的开发工具创建恶意插件和扩展。
展望未来,制定全面的安全措施来应对入口点攻击至关重要。通过了解和解决这些风险,我们可以努力打造更安全的 Python 打包环境,保护个人开发人员和企业系统免受复杂的供应链攻击。
作为 Checkmarx 供应链安全解决方案的一部分,我们的研究团队持续监控开源软件生态系统中的可疑活动。我们跟踪并标记可能表明存在不法行为的“信号”,包括可疑的入口点,并及时向客户发出警报,帮助他们防范潜在威胁。
原文始发于微信公众号(Ots安全):这种新的供应链攻击技术可以木马化所有 CLI 命令
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论