2024年12月24号,天问Python供应链威胁监测模块发现PyPI中出现了许多利用模块替代攻击的恶意包,这些恶意包采用伪装包名诱骗用户下载。在安装过程中,包中内置的第三方模块(例如requests)会替代用户主机原有同名模块,导致用户被攻击。模块替代攻击已经成为了PyPI中一个不可忽视的威胁。

天问供应链威胁监测模块是奇安信技术研究星图实验室研发的“天问”软件供应链安全分析平台的子模块,”天问“分析平台对Python、npm等主流的开发生态进行了长期、持续的监测,发现了大量的恶意包和攻击行为。

1. 伪造包名攻击(TypoSquatting)

2024年12月24日,天问Python供应链威胁监测模块发现了PyPI出现了许多伪造包名的软件包,如reqesst。这些恶意包通过构建与流行软件包requests(日均下载量超过1000万[1])极其相近的拼写,来诱导用户下载。

这些恶意包的版本均为2.32.3,观察流行包requests的发布情况,我们发现其目前的版本也是2.32.3。由此可以确定,这是有针对性的伪造包名攻击。

image-20250106205250826

由上图可知,恶意包和正常包的代码文件结构一致。我们分析了常见的攻击入口,setup.py__init__.py并未发现直接的攻击代码。但鉴于包名和包的内容基本都在刻意伪装成正常代码,因此我们对所有的代码进行了细致分析,最终在reqesst-2.32.3/src/requests/models.py的第312行、313行发现了藏匿的恶意代码。

image-20250106105850678

其使用了base64编码来隐藏恶意代码的真实意图,通过解码可得如下的url

1
https[:]//www.dropbox.com/scl/fi/o5sege0jphqd9gjxgeiia/asd2.exe?rlkey=44p2rf6h6i55z74hsj9p682cj&st=1p1np9nx&dl=1

asd2.exe是Windows下流行的反恶意软件工具Anvi Smart Defender关联的可执行文件。攻击者尝试将从dropbox.com中下载恶意的asd2.exe来替代用户机器上原有的asd2.exe来进行攻击。目前该文件已无法下载。

我们细致分析了models.py中恶意代码的触发方式,在reqesst-2.32.3/src/requests/__init__.pymodels会被导入,如下所示。

image-20250106112228662

因此,当用户尝试在代码中import requests,就会触发攻击代码。至于为什么攻击者的恶意模块requests能够替代用户原有的良性requests模块,这与Python包安装的特性有关。此类攻击我们称之为模块替代攻击。我们在之前的文章中也讨论过此类攻击事件[2]。

2. 模块替代攻击

在这一节,我们会讲解Python模块替代攻击的具体原理并展示其实际攻击效果。

2.1 攻击原理

当用户使用pip下载第三方Python包时,这些包会默认安装在site-packages目录之中。Python包中会包含一个或多个模块,这些模块会被统一放置在site-packages目录下。虽然不同的用户无法在PyPI软件源中发布相同名称的软件包,但包中的模块可以同名。因此,包含相同模块名的不同包在安装时,会发生模块冲突问题。目前pip默认安装策略是使用后安装的模块覆盖替代原有同名模块。如下图所示,利用这个特性,攻击者可以完成模块替代攻击。

image-worflow

2.2 攻击效果展示

为了更好地展示这种攻击效果,我们设计了两个简单的示例python包,pkg_goodpkg_bad。他们均包含相同的模块test_module

  1. 我们首先安装pkg_good,包的结构如下所示

    image-20250106153458476

    核心代码如下所示

    __init__.py

    1
    from .demo import test

    demo.py

    1
    2
    3
    def test():
    print('demo for test')
    print('hello world!')

    实际导入效果如下所示,虽然我们只导入了test函数,但是print('hello world!')demo.py的全局作用域下,所以导入时会默认执行。这种触发方式与上面实际攻击案例一致。

    image-20250106155025094

  2. 然后我们安装pkg_bad,两个包的核心差异在于demo.py文件,如下所示

    demo.py

    1
    2
    3
    def test():
    print('demo for test')
    print('You are hacked!')

    安装执行效果如下所示。

    image-20250106160405351

    从上图我们可以观察到,test_module已被替换为了恶意的模块。这种攻击对于普通用户难以察觉,极易受到攻击影响。

模块替代攻击本质上是利用了Python设计中固有的特性,所以可以预见这类攻击在未来一段时间将会不断出现。现在PyPI及GitHub中的许多包都存在模块冲突的问题,会影响用户的正常使用甚至威胁用户安全[3]。对于用户而言,要尽量避免下载不可信的第三方软件包。Python的包名和模块名并不一致,所以在代码编写过程中出现模块缺少问题,要确认好实际对应的包名,避免误下载恶意软件包。

3. 大模型分析测试

通过对当前恶意包的分析,我们发现人工去校验查看此类攻击非常繁琐。恶意代码隐藏在大量正常代码之中,因此我们尝试使用天穹沙箱的TQ-GPT[4]来辅助我们的分析。

提示词如下所示:

1
判断下面python代码是否包含潜在恶意代码,是则输出可疑代码,并给出理由。 否,直接回复"否"

结果如下所示:

image-20250106181307829

从上图可知,TQ-GPT能很好地辅助我们查找恶意代码的位置。这对于繁琐的代码分析无疑提供了有力的支持,大大减少了安全分析人员的工作负担。天问监测系统正在逐步尝试与TQ-GPT结合,更加快速高效地识别定位开源生态中的恶意代码。

4. 总结

模块替代攻击利用了Python软件包安装的特性,使得攻击者可以将恶意篡改后的模块替换用户主机中的同名模块。当用户正常导入该模块时,就会触发恶意代码,从而遭受攻击。此类攻击极其难以防范,从上文分析可知,攻击者只需要在大量正常代码中插入少量恶意代码即可完成攻击,而且对于用户完全透明。虽然,本文的例子结合了伪造包名来实施攻击,会引起安全分析人员的察觉。但攻击者也可以发布正常的软件包,并在其中先嵌入正常的第三方模块,通过日常维护积累大量用户后实施模块替代攻击。

目前这类攻击没有非常好的防护手段,需要用户提高警惕,不要轻易下载不可信第三方库。天问Python供应链威胁监测模块[5]也会对PyPI进行持续监测,并及时发布相关报告。

恶意包列表

包名 版本 上传时间
reqesst 2.32.2 2024-12-24
requeszs 2.32.2 2024-12-24
reqest 2.32.2 2024-12-24
rreqest 2.32.2 2024-12-24
reqeuts 2.32.2 2024-12-25
reqzest 2.32.2 2024-12-25
reqiest 2.32.2 2024-12-25
requesr 2.32.2 2024-12-25

参考链接

[1] requests包下载量统计 https://pypistats.org/packages/requests

[2] 【天问】PyPI “特洛伊木马” https://tianwen.qianxin.com/blog/2024/02/05/pypi-trojan

[3] ModuleGuard: Understanding and Detecting Module Conflicts in Python Ecosystem. https://arxiv.org/pdf/2401.02090

[4] 【天穹】TQ-GPT沙箱智能助理,样本分析新体验! https://tianwen.qianxin.com/blog/2024/04/11/tq-sandbox-TQGPT-online/

[5] “天问”软件供应链安全分析平台 https://tianwen.qianxin.com/