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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/scan.js +54 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hulud-party-scanner",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "bin": {
5
5
  "hulud-party-scanner": "./scan.js"
6
6
  },
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 = "./compromised-libs.txt";
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 for correlated exfiltration (only for text files)
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.");