muaddib-scanner 2.11.128 → 2.11.129

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muaddib-scanner",
3
- "version": "2.11.128",
3
+ "version": "2.11.129",
4
4
  "description": "Supply-chain threat detection & response for npm & PyPI/Python",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "target": "node_modules",
3
- "timestamp": "2026-06-20T10:44:00.296Z",
3
+ "timestamp": "2026-06-24T09:39:17.849Z",
4
4
  "threats": [
5
5
  {
6
6
  "type": "string_mutation_obfuscation",
@@ -44,6 +44,7 @@ const HIGH_CONFIDENCE_MALICE_TYPES = new Set([
44
44
  'download_exec_binary', // download+chmod+exec
45
45
  'reverse_shell', // reverse shell (always malicious)
46
46
  'crypto_staged_payload', // decrypt->eval staged payload chain
47
+ 'crypto_exfil', // harvest + encrypt(RSA/AES) + network to non-benign dest (litellm/Hades)
47
48
  'intent_credential_exfil', // intra-file credential->network
48
49
  'intent_command_exfil', // intra-file command->network
49
50
  'cross_file_dataflow', // proven taint cross-modules
@@ -693,6 +693,12 @@ const PLAYBOOKS = {
693
693
  'puis execute le resultat via eval/Function. Pattern buildrunner-dev: payload malveillant cache dans une image PNG. ' +
694
694
  'Isoler immediatement. Analyser le payload dechiffre. Supprimer le package.',
695
695
 
696
+ crypto_exfil:
697
+ 'CRITIQUE: Exfiltration chiffree de secrets. Le code aspire des credentials/env, les chiffre (RSA publicEncrypt / AES createCipheriv, ' +
698
+ 'souvent une enveloppe hybride avec cle publique embarquee) puis les envoie sur le reseau vers une destination non first-party. ' +
699
+ 'Le chiffrement masque le payload aux inspections DLP/taint (pattern litellm/Hades). Isoler immediatement. Identifier la cle publique ' +
700
+ 'et le host de destination (C2). Revoquer les secrets potentiellement exposes. Supprimer le package.',
701
+
696
702
  download_exec_binary:
697
703
  'CRITIQUE: Pattern download-execute detecte. Le code telecharge un binaire, le rend executable (chmod 755), ' +
698
704
  'puis l\'execute via execSync. Dropper deguise en compilation native addon (NeoShadow pattern). ' +
@@ -809,6 +809,20 @@ const RULES = {
809
809
  ],
810
810
  mitre: 'T1195.002'
811
811
  },
812
+ crypto_exfil: {
813
+ id: 'MUADDIB-COMPOUND-019',
814
+ name: 'Hybrid-Crypto Credential Exfiltration',
815
+ severity: 'CRITICAL',
816
+ confidence: 'high',
817
+ domain: 'malware',
818
+ description: 'Harvesting de secrets (env/credentials) + primitive de chiffrement (RSA publicEncrypt / AES createCipheriv) + envoi reseau dans le MEME fichier, vers une destination qui n\'est PAS entierement first-party/de confiance (destAllBenign faux). Les secrets voles sont chiffres AVANT exfil pour echapper a l\'inspection DLP et au taint statique — pattern litellm / Hades (juin 2026). Les libs E2E/licence/telemetry legitimes chiffrent+envoient mais n\'aspirent pas des credentials dans le blob et ne postent que vers leur propre provider (supprime par destAllBenign). Miroir cote chiffrement de fetch_decrypt_exec.',
819
+ references: [
820
+ 'https://attack.mitre.org/techniques/T1560/',
821
+ 'https://attack.mitre.org/techniques/T1041/',
822
+ 'https://attack.mitre.org/techniques/T1552/'
823
+ ],
824
+ mitre: 'T1560'
825
+ },
812
826
 
813
827
  // Package.json script patterns
814
828
  curl_pipe_sh: {
@@ -1502,6 +1502,20 @@ function handleCallExpression(node, ctx) {
1502
1502
  file: ctx.relFile
1503
1503
  });
1504
1504
  }
1505
+ // crypto_exfil (encrypt side): a REAL call to an encryption primitive sets the file flag
1506
+ // consumed by the same-file crypto_exfil compound (handle-post-walk.js). AST-based — a string
1507
+ // or regex literal that merely contains "createCipheriv(" is not a CallExpression and won't trip it.
1508
+ if (propName === 'createCipher' || propName === 'createCipheriv' || propName === 'publicEncrypt') {
1509
+ ctx.hasCryptoEncipher = true;
1510
+ } else if (propName === 'encrypt') {
1511
+ // WebCrypto: crypto.subtle.encrypt(...) — gate on the `.subtle.` receiver so a generic
1512
+ // foo.encrypt() (ORMs, mailers, many libs expose .encrypt) does not set the flag.
1513
+ const obj = node.callee.object;
1514
+ if (obj && obj.type === 'MemberExpression' && obj.property &&
1515
+ (obj.property.name === 'subtle' || obj.property.value === 'subtle')) {
1516
+ ctx.hasCryptoEncipher = true;
1517
+ }
1518
+ }
1505
1519
  if (propName === '_compile') {
1506
1520
  // Context-aware gating: only flag _compile when the Module API is plausibly in scope.
1507
1521
  // Custom class methods (e.g. blessed's Tput.prototype._compile) are not malware.
@@ -366,6 +366,35 @@ function handlePostWalk(ctx) {
366
366
  }
367
367
  }
368
368
 
369
+ // crypto_exfil (RSA+AES hybrid exfil — litellm/Hades 2026): secret harvesting + an encryption
370
+ // primitive (RSA publicEncrypt / AES createCipheriv) + a network send, in the SAME file, to a
371
+ // destination that is NOT entirely first-party/trusted. The encrypt step wraps the stolen
372
+ // secrets so DLP and static taint miss the payload. Legit E2E/license/telemetry SDKs encrypt
373
+ // and send, but do not also harvest credentials/env into the blob, and the benign ones post
374
+ // only to their own provider (suppressed by destAllBenign). Requires a non-LOW harvest threat
375
+ // already emitted in this file — mirror of fetch_decrypt_exec (decrypt-side loader).
376
+ // destAllBenign is computed once above, at the credential_regex_harvest emission site.
377
+ const CRYPTO_EXFIL_HARVEST_TYPES = new Set([
378
+ 'env_access', 'env_charcode_reconstruction', 'env_harvesting_dynamic',
379
+ 'sensitive_string', 'suspicious_dataflow', 'credential_regex_harvest',
380
+ 'credential_command_exec', 'npm_token_steal'
381
+ ]);
382
+ if (ctx.hasCryptoEncipher && ctx.hasNetworkCallInFile && !destAllBenign) {
383
+ const cryptoExfilHarvest = ctx.threats.some(t =>
384
+ t.file === ctx.relFile && t.severity !== 'LOW' && CRYPTO_EXFIL_HARVEST_TYPES.has(t.type)
385
+ );
386
+ if (cryptoExfilHarvest) {
387
+ ctx.threats.push({
388
+ type: 'crypto_exfil',
389
+ severity: 'CRITICAL',
390
+ message: 'Hybrid-crypto exfiltration: secret harvesting + ' +
391
+ (ctx.hasEmbeddedPublicKey ? 'embedded RSA/EC public key + ' : '') +
392
+ 'encryption (RSA/AES) + network send in same file — harvested secrets are encrypted before exfil to evade DLP/taint inspection (litellm/Hades pattern).',
393
+ file: ctx.relFile
394
+ });
395
+ }
396
+ }
397
+
369
398
  // GlassWorm: Unicode variation selector decoder = .codePointAt + variation selector constants
370
399
  // CRITICAL if combined with eval/exec (GlassWorm always uses dynamic execution),
371
400
  // MEDIUM otherwise (.codePointAt + 0xFE00 is legitimate Unicode processing in fonts/text libs)
@@ -208,6 +208,17 @@ function analyzeFile(content, filePath, basePath) {
208
208
  // Safe domain exclusion: if ALL URLs in file are from known registries, suppress download_exec_binary
209
209
  fetchOnlySafeDomains: false, // computed below after URL extraction
210
210
  hasCryptoDecipher: /\bcreateDecipher(iv)?\s*\(/.test(content),
211
+ // crypto_exfil (RSA+AES hybrid exfil — litellm/Hades 2026): encrypt-side mirror of
212
+ // hasCryptoDecipher. Set TRUE in handle-call-expression.js on a REAL call to an encryption
213
+ // primitive (createCipher(iv) = AES, publicEncrypt = RSA pubkey-wrap, subtle.encrypt =
214
+ // WebCrypto). AST-based on purpose, NOT a content regex: a detector/linter/doc that merely
215
+ // contains the STRING "createCipheriv(" must not trip it (self-scan & meta-tooling FP).
216
+ // Feeds the same-file crypto_exfil compound in handle-post-walk.js.
217
+ hasCryptoEncipher: false,
218
+ // Embedded RSA/EC public key (PEM SPKI/PKCS1) — an attacker's hardcoded recipient key for
219
+ // the exfil envelope. Enrichment signal only (a PEM public key alone — JWT/JWK verify,
220
+ // signature checks — is benign and extremely common, so it is never flagged on its own).
221
+ hasEmbeddedPublicKey: /-----BEGIN (?:RSA |EC )?PUBLIC KEY-----/.test(content),
211
222
  // Wave 4: native addon camouflage signals
212
223
  hasRequireNodeFile: false,
213
224
  hasExecSyncCall: false,