MAL-2026-6309
Vulnerability from ossf_malicious_packages
Published
2026-06-22 12:00
Modified
2026-06-23 19:34
Summary
Malicious code in @nullzero/urlcat (npm)
Details

@nullzero/urlcat (version 1.4.2, published by nullzero-rlnozk@wshu.net) is a trojanized npm package belonging to the wshu.net credential-stealer campaign. The campaign published trojanized look-alike utility packages across 12+ scopes whose publisher accounts all follow the pattern -<6 random chars>@wshu.net, with every scope created on June 4, 2026 in a ~40-minute burst. Like the other packages in the campaign, it declares a postinstall hook ("node lib/encoder.js") that runs a bundled payload file automatically on npm install. The campaign payload is a Chromium browser credential stealer that reads Chromium Cookies and Login Data, decrypts saved passwords protected by AES-256-GCM (the v10/v11 app-bound key schemes), and exfiltrates them over HTTPS using a spoofed Mozilla/5.0 user agent, hidden behind javascript-obfuscator obfuscation (hex identifiers, a while (!![]) array-rotation IIFE, base64+RC4 string decoding, control-flow flattening, and runtime-decrypted module resolution). This package was unpublished from npm before the payload could be captured, so its specific payload was not independently verified; it is reported on the basis of its membership in the wshu.net campaign (matching publisher email pattern, scope-creation burst, and postinstall execution pattern).


-= Per source details. Do not edit below this line.=-

Source: amazon-inspector (2c007ea1ba0e4bcd680cc3770361eefead0673eca418787720fa65c8c71a2e57)

Package @nullzero/urlcat impersonates the legitimate urlcat URL-builder library — same advertised cat(base, path, params) API, README copied from upstream, and package.json.repository.url points to git+https://github.com/balazsbotond/urlcat.git (the real upstream maintainer's repo, not the nullzero publisher's). The package main lib/index.js line 64 calls encoder.runPrepare() at the top of every invocation of the exported cat() function. lib/encoder.js is a 263 KB obfuscator.io-packed file (rotated 1176-entry string array, RC4 decoder _0x2f0d, control-flow flattening) — far beyond anything a tiny URL composer requires. Decoded control flow in lib/encoder.js selects a platform-specific binary candidate (branches on process.platform === 'win32' to 'win.js' / a bun-style executable, otherwise a node-typed binary), constructs a destination under os.tmpdir(), downloads it over https.request following up to 5 redirects with User-Agent: node-installer, sha256-checks against a .meta JSON sidecar, and then spawns the dropped binary (or re-execs process.execPath against it) detached + unref'd, with a private env-var marker (__7D0A53...). The encoder also installs no-op handlers for uncaughtException, unhandledRejection, and SIGINT to suppress crashes, performs obfuscator.io-style debugger-detection (Function('debugger') regex self-check), and re-spawns the current node when run interactively so the payload runs only in the detached child. A URL-builder library has no legitimate need for a 263 KB obfuscated sibling, a platform-specific binary download, anti-debug guards, or a detached child re-exec. Any consumer who calls cat() triggers arbitrary code execution from an attacker-controlled binary on their machine.

CWE
  • CWE-506 - The product contains code that appears to be malicious in nature.
  • CWE-506 - The product contains code that appears to be malicious in nature.
  • CWE-506 - The product contains code that appears to be malicious in nature.
  • CWE-506 - The product contains code that appears to be malicious in nature.
Credits

{
  "affected": [
    {
      "database_specific": {
        "cwes": [
          {
            "cweId": "CWE-506",
            "description": "The product contains code that appears to be malicious in nature.",
            "name": "Embedded Malicious Code"
          },
          {
            "cweId": "CWE-506",
            "description": "The product contains code that appears to be malicious in nature.",
            "name": "Embedded Malicious Code"
          },
          {
            "cweId": "CWE-506",
            "description": "The product contains code that appears to be malicious in nature.",
            "name": "Embedded Malicious Code"
          },
          {
            "cweId": "CWE-506",
            "description": "The product contains code that appears to be malicious in nature.",
            "name": "Embedded Malicious Code"
          }
        ],
        "indicators": {
          "evidence_files": [
            {
              "path": "lib/encoder.js",
              "sha256": "ba5e1031746c82983a671e0b7b03d9f56b4a1258b7516ddfe286cdb487c363c3",
              "tlsh": "2de0688d38f128706e594357b11b1c9172eda2081301a1108fce8bd8278453913e282f"
            }
          ],
          "package_integrity": [
            {
              "filename": "urlcat-1.4.3.tgz",
              "hashes": {
                "sha1": "0ef51eb3b28e9fb419f6578797322c13b9786c8e",
                "sha512_sri": "sha512-pj+CRlsAgGbtu8h74+eNrBKKmWwViQjSMDJZyUthSGRkEihCVKzVOid5ZPIyYcy59Nu5fcyl+SqDhCiGFKGQNw=="
              }
            }
          ]
        }
      },
      "package": {
        "ecosystem": "npm",
        "name": "@nullzero/urlcat"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            }
          ],
          "type": "SEMVER"
        }
      ],
      "versions": [
        "1.4.1",
        "1.4.0",
        "1.4.2",
        "1.4.3"
      ]
    }
  ],
  "credits": [
    {
      "contact": [
        "inspector-research@amazon.com"
      ],
      "name": "Amazon Inspector",
      "type": "FINDER"
    },
    {
      "contact": [
        "https://safedep.io"
      ],
      "name": "SafeDep",
      "type": "FINDER"
    }
  ],
  "database_specific": {
    "malicious-packages-origins": [
      {
        "id": "IN-MAL-2026-007314",
        "import_time": "2026-06-23T16:54:17.120393864Z",
        "modified_time": "2026-06-23T16:22:57Z",
        "sha256": "0de6113e5b3433808a62d127830818bac2391c8f20882040433a811553668db7",
        "source": "amazon-inspector",
        "versions": [
          "1.4.1"
        ]
      },
      {
        "id": "IN-MAL-2026-007323",
        "import_time": "2026-06-23T16:54:17.713612334Z",
        "modified_time": "2026-06-23T16:23:05Z",
        "sha256": "517e344b6aaf2fb41a8050673d5500fbaed6cdcf07578f5aba85c6fb0f5c0e59",
        "source": "amazon-inspector",
        "versions": [
          "1.4.0"
        ]
      },
      {
        "id": "IN-MAL-2026-007313",
        "import_time": "2026-06-23T16:54:17.041703632Z",
        "modified_time": "2026-06-23T16:22:56Z",
        "sha256": "9c1a822d6369b344bb87e639f84077b5d6f096b02d36cb126754b30c32f2984e",
        "source": "amazon-inspector",
        "versions": [
          "1.4.2"
        ]
      },
      {
        "id": "IN-MAL-2026-007315",
        "import_time": "2026-06-23T16:54:17.205940206Z",
        "modified_time": "2026-06-23T16:22:58Z",
        "sha256": "2c007ea1ba0e4bcd680cc3770361eefead0673eca418787720fa65c8c71a2e57",
        "source": "amazon-inspector",
        "versions": [
          "1.4.3"
        ]
      }
    ]
  },
  "details": "@nullzero/urlcat (version 1.4.2, published by nullzero-rlnozk@wshu.net) is a trojanized npm package belonging to the wshu.net credential-stealer campaign. The campaign published trojanized look-alike utility packages across 12+ scopes whose publisher accounts all follow the pattern \u003cscope\u003e-\u003c6 random chars\u003e@wshu.net, with every scope created on June 4, 2026 in a ~40-minute burst. Like the other packages in the campaign, it declares a postinstall hook (\"node lib/encoder.js\") that runs a bundled payload file automatically on npm install. The campaign payload is a Chromium browser credential stealer that reads Chromium Cookies and Login Data, decrypts saved passwords protected by AES-256-GCM (the v10/v11 app-bound key schemes), and exfiltrates them over HTTPS using a spoofed Mozilla/5.0 user agent, hidden behind javascript-obfuscator obfuscation (hex identifiers, a while (!![]) array-rotation IIFE, base64+RC4 string decoding, control-flow flattening, and runtime-decrypted module resolution). This package was unpublished from npm before the payload could be captured, so its specific payload was not independently verified; it is reported on the basis of its membership in the wshu.net campaign (matching publisher email pattern, scope-creation burst, and postinstall execution pattern).\n\n---\n_-= Per source details. Do not edit below this line.=-_\n\n## Source: amazon-inspector (2c007ea1ba0e4bcd680cc3770361eefead0673eca418787720fa65c8c71a2e57)\nPackage `@nullzero/urlcat` impersonates the legitimate `urlcat` URL-builder library \u2014 same advertised `cat(base, path, params)` API, README copied from upstream, and `package.json.repository.url` points to `git+https://github.com/balazsbotond/urlcat.git` (the real upstream maintainer\u0027s repo, not the `nullzero` publisher\u0027s). The package main `lib/index.js` line 64 calls `encoder.runPrepare()` at the top of every invocation of the exported `cat()` function. `lib/encoder.js` is a 263 KB obfuscator.io-packed file (rotated 1176-entry string array, RC4 decoder `_0x2f0d`, control-flow flattening) \u2014 far beyond anything a tiny URL composer requires. Decoded control flow in `lib/encoder.js` selects a platform-specific binary candidate (branches on `process.platform === \u0027win32\u0027` to `\u0027win.js\u0027` / a bun-style executable, otherwise a node-typed binary), constructs a destination under `os.tmpdir()`, downloads it over `https.request` following up to 5 redirects with `User-Agent: node-installer`, sha256-checks against a `.meta` JSON sidecar, and then `spawn`s the dropped binary (or re-execs `process.execPath` against it) detached + unref\u0027d, with a private env-var marker (`__7D0A53...`). The encoder also installs no-op handlers for `uncaughtException`, `unhandledRejection`, and `SIGINT` to suppress crashes, performs obfuscator.io-style debugger-detection (`Function(\u0027debugger\u0027)` regex self-check), and re-spawns the current node when run interactively so the payload runs only in the detached child. A URL-builder library has no legitimate need for a 263 KB obfuscated sibling, a platform-specific binary download, anti-debug guards, or a detached child re-exec. Any consumer who calls `cat()` triggers arbitrary code execution from an attacker-controlled binary on their machine.\n",
  "id": "MAL-2026-6309",
  "modified": "2026-06-23T19:34:37Z",
  "published": "2026-06-22T12:00:00Z",
  "references": [
    {
      "type": "PACKAGE",
      "url": "https://www.npmjs.com/package/@nullzero/urlcat/v/1.4.1"
    },
    {
      "type": "PACKAGE",
      "url": "https://www.npmjs.com/package/@nullzero/urlcat/v/1.4.0"
    },
    {
      "type": "PACKAGE",
      "url": "https://www.npmjs.com/package/@nullzero/urlcat/v/1.4.2"
    },
    {
      "type": "PACKAGE",
      "url": "https://www.npmjs.com/package/@nullzero/urlcat/v/1.4.3"
    },
    {
      "type": "REPORT",
      "url": "https://safedep.io/wshu-net-npm-credential-stealer-campaign/"
    },
    {
      "type": "PACKAGE",
      "url": "https://www.npmjs.com/package/@nullzero/urlcat"
    }
  ],
  "schema_version": "1.7.4",
  "summary": "Malicious code in @nullzero/urlcat (npm)"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…