hulud-party-scanner 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/scan.js +54 -2
package/package.json
CHANGED
package/scan.js
CHANGED
|
@@ -6,7 +6,7 @@ const crypto = require('crypto');
|
|
|
6
6
|
const { execSync } = require('child_process');
|
|
7
7
|
|
|
8
8
|
// --- Configuration ---
|
|
9
|
-
const COMPROMISED_LIST_URL = "
|
|
9
|
+
const COMPROMISED_LIST_URL = "https://raw.githubusercontent.com/migohe14/hulud-scanner/refs/heads/main/compromised-libs.txt";
|
|
10
10
|
const MALICIOUS_HASHES = new Set([
|
|
11
11
|
"46faab8ab153fae6e80e7cca38eab363075bb524edd79e42269217a083628f09",
|
|
12
12
|
"de0e25a3e6c1e1e5998b306b7141b3dc4c0088da9d7bb47c1c00c91e6e4f85d6",
|
|
@@ -26,6 +26,17 @@ const COMPROMISED_NAMESPACES = [
|
|
|
26
26
|
];
|
|
27
27
|
const EXFIL_PATTERNS = ['webhook.site', 'bb8ca5f6-4175-45d2-b042-fc9ebb8170b7', 'exfiltrat'];
|
|
28
28
|
const ENV_PATTERNS = ['process\\.env', 'os\\.environ', 'getenv', 'AWS_ACCESS_KEY', 'GITHUB_TOKEN', 'NPM_TOKEN'];
|
|
29
|
+
const MALICIOUS_FILENAMES = new Set([
|
|
30
|
+
'bun_environment.js',
|
|
31
|
+
'trufflehog',
|
|
32
|
+
'trufflehog.exe'
|
|
33
|
+
]);
|
|
34
|
+
const MALICIOUS_COMMAND_PATTERNS = [
|
|
35
|
+
'bun.sh/install', // Catches both curl and powershell variants
|
|
36
|
+
'del /F /Q /S "%USERPROFILE%\\*"',
|
|
37
|
+
'shred -uvz -n 1',
|
|
38
|
+
'cipher /W:%USERPROFILE%'
|
|
39
|
+
];
|
|
29
40
|
|
|
30
41
|
// --- Console Colors ---
|
|
31
42
|
const colors = {
|
|
@@ -248,6 +259,8 @@ function scanProjectFiles(allFiles, projectRoot) {
|
|
|
248
259
|
namespaceMatches: new Set(),
|
|
249
260
|
hookMatches: [],
|
|
250
261
|
correlatedExfil: [],
|
|
262
|
+
filenameMatches: [],
|
|
263
|
+
commandMatches: [],
|
|
251
264
|
};
|
|
252
265
|
|
|
253
266
|
const pkgJsonFiles = allFiles.filter(f => path.basename(f) === 'package.json');
|
|
@@ -281,6 +294,7 @@ function scanProjectFiles(allFiles, projectRoot) {
|
|
|
281
294
|
log.info("Scanning file signatures and for exfiltration patterns...");
|
|
282
295
|
const envRegex = new RegExp(ENV_PATTERNS.join('|'));
|
|
283
296
|
const exfilRegex = new RegExp(EXFIL_PATTERNS.join('|'));
|
|
297
|
+
const commandRegex = new RegExp(MALICIOUS_COMMAND_PATTERNS.join('|').replace(/%/g, '%').replace(/\*/g, '\\*'), 'i');
|
|
284
298
|
|
|
285
299
|
for (const file of allFiles) {
|
|
286
300
|
try {
|
|
@@ -295,12 +309,22 @@ function scanProjectFiles(allFiles, projectRoot) {
|
|
|
295
309
|
findings.hashMatches.push(path.relative(projectRoot, file));
|
|
296
310
|
}
|
|
297
311
|
|
|
298
|
-
// 2. Check
|
|
312
|
+
// 2. Check filename
|
|
313
|
+
if (MALICIOUS_FILENAMES.has(path.basename(file))) {
|
|
314
|
+
findings.filenameMatches.push(path.relative(projectRoot, file));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// 3. Check for correlated exfiltration and malicious commands (only for text files)
|
|
299
318
|
if (file.endsWith('.js') || file.endsWith('.ts') || file.endsWith('.json') || file.endsWith('.sh') || file.endsWith('.yml')) {
|
|
300
319
|
const content = fileBuffer.toString('utf-8');
|
|
301
320
|
const hasEnv = envRegex.test(content);
|
|
302
321
|
const hasExfil = exfilRegex.test(content);
|
|
303
322
|
|
|
323
|
+
// Check for malicious commands
|
|
324
|
+
if (commandRegex.test(content)) {
|
|
325
|
+
findings.commandMatches.push(path.relative(projectRoot, file));
|
|
326
|
+
}
|
|
327
|
+
|
|
304
328
|
if (hasEnv && hasExfil) {
|
|
305
329
|
findings.correlatedExfil.push(path.relative(projectRoot, file));
|
|
306
330
|
}
|
|
@@ -386,6 +410,7 @@ async function main() {
|
|
|
386
410
|
log.header("Module 2: Project Structure & Content Analysis");
|
|
387
411
|
const allFiles = getAllFiles(projectRoot);
|
|
388
412
|
const fileScanFindings = scanProjectFiles(allFiles, projectRoot);
|
|
413
|
+
const homeDirFindings = scanHomeDirectory();
|
|
389
414
|
|
|
390
415
|
// --- Reporting ---
|
|
391
416
|
log.header("Scan Report");
|
|
@@ -401,6 +426,24 @@ async function main() {
|
|
|
401
426
|
report += " NOTE: This is a definitive indicator of compromise. Immediate investigation is required.\n\n";
|
|
402
427
|
}
|
|
403
428
|
|
|
429
|
+
if (homeDirFindings.length > 0) {
|
|
430
|
+
issuesFound = true;
|
|
431
|
+
report += `${colors.RED}🚨 HIGH RISK: Malicious Artifacts Found in Home Directory${colors.RESET}\n`;
|
|
432
|
+
homeDirFindings.forEach(match => {
|
|
433
|
+
report += ` - ${colors.YELLOW}${match}${colors.RESET}\n`;
|
|
434
|
+
});
|
|
435
|
+
report += " NOTE: These artifacts are used to store and execute malicious tools.\n\n";
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (fileScanFindings.filenameMatches.length > 0) {
|
|
439
|
+
issuesFound = true;
|
|
440
|
+
report += `${colors.RED}🚨 HIGH RISK: Known Malicious Filename Detected${colors.RESET}\n`;
|
|
441
|
+
fileScanFindings.filenameMatches.forEach(match => {
|
|
442
|
+
report += ` - File: ${colors.YELLOW}${match}${colors.RESET}\n`;
|
|
443
|
+
});
|
|
444
|
+
report += " NOTE: These filenames are associated with malicious scripts.\n\n";
|
|
445
|
+
}
|
|
446
|
+
|
|
404
447
|
if (fileScanFindings.correlatedExfil.length > 0) {
|
|
405
448
|
issuesFound = true;
|
|
406
449
|
report += `${colors.RED}🚨 HIGH RISK: Environment Scanning with Exfiltration Detected${colors.RESET}\n`;
|
|
@@ -437,6 +480,15 @@ async function main() {
|
|
|
437
480
|
report += " NOTE: 'postinstall' scripts can execute arbitrary commands and require review.\n\n";
|
|
438
481
|
}
|
|
439
482
|
|
|
483
|
+
if (fileScanFindings.commandMatches.length > 0) {
|
|
484
|
+
issuesFound = true;
|
|
485
|
+
report += `${colors.YELLOW}⚠️ MEDIUM RISK: Suspicious Commands Found in Files${colors.RESET}\n`;
|
|
486
|
+
fileScanFindings.commandMatches.forEach(match => {
|
|
487
|
+
report += ` - File: ${colors.YELLOW}${match}${colors.RESET}\n`;
|
|
488
|
+
});
|
|
489
|
+
report += " NOTE: These files contain commands known to be used for malicious purposes.\n\n";
|
|
490
|
+
}
|
|
491
|
+
|
|
440
492
|
if (issuesFound) {
|
|
441
493
|
console.log(report);
|
|
442
494
|
log.error("Scan complete. Actionable issues were found.");
|