2,9 millioner nedlastinger i uka gikk gjennom fem trojaniserte @asyncapi-pakker som ble publisert på npm 14. juli, ifølge sikkerhetsselskapet Aikido. Alene stod @asyncapi/specs for rundt 2,7 millioner av dem. Skadekoden lå ikke i et npm-installasjonshook, men i moduler som kjører under vanlig bruk, blant annet inngangspunktet til specs. Et rent require() er nok til å trigge den: koden starter en frittstående Node-prosess som henter en kryptert loader fra IPFS, skriver den til disk som sync.js og kjører den videre i bakgrunnen.
Angriperne skaffet seg publiseringstilgang uten å knekke et eneste passord. AsyncAPI-generatoren brukte en GitHub Actions-arbeidsflyt med pull_request_target, en velkjent felle der arbeidsflyten kjører med tilgang til repoets hemmeligheter selv når den sjekker ut kode fra en fremmed pull request. En bidragsyter meldte svakheten og åpnet en fiks (PR #2092) allerede 17. mai. Den lå fortsatt uflettet nesten to måneder senere da angrepet kom. Klokka 05:08 UTC 14. juli åpnet angriperen 37 pull requests på én gang. Én av dem lekket npm-tokenet til en dead-drop på rentry[.]co, og åtte minutter senere var arbeidsflyten ferdig. Første kompromitterte pakke lå ute 07:10 UTC.
«Vi kan ikke ut fra bevisene avgjøre om det innledende innbruddet og nyttelasten stammer fra én aktør eller flere. Miasma-merkingen kan være gjenbruk av kode, etterligning eller bevisst villspor.» — Aikido Security
Nyttelasten ender i en vedvarende implantat med et ekte fjernskall. Selve rammeverket kaller seg «M-RED-TEAM v6.4» i egne kodekommentarer, mens artefaktnavn peker mot et eldre verktøysett kalt Miasma. En forsker hos SafeDep bemerket likevel at denne bygningen skiller seg fra det gamle Miasma: den er Node-basert med secp256k1 og AES-GCM, bruker HTTP-basert C2, og har både legitimasjonstyveri og selvspredning liggende i koden, men slått av i denne versjonen. Skallet alene er nok til å hente ut data og kjøre vilkårlige kommandoer.
Lærdommen er at et bygge-token er like verdifullt som en signeringsnøkkel. Når pull_request_target gir en fremmed PR tilgang til repo-hemmeligheter, holder det ikke å stole på at koden i pull requesten er godartet. Og siden skadekoden her kjørte ved modulinnlasting og ikke i et lifecycle-hook, fanger ikke --ignore-scripts den.
Hva bør du gjøre?
- Sjekk lockfilen for de fem kompromitterte versjonene:
@asyncapi/specs@6.11.2(og6.11.2-alpha.1),@asyncapi/generator@3.3.1,@asyncapi/generator-helpers@1.1.1og@asyncapi/generator-components@0.7.1. Pinn til en tidligere kjent-god versjon. - Let etter
sync.jsi NodeJS-datamappa (~/Library/Application Support/NodeJSpå macOS,%LOCALAPPDATA%\NodeJSpå Windows,~/.local/share/NodeJSpå Linux) og drep frittstående node-prosesser du ikke kjenner igjen. - Roter alle legitimasjoner og API-nøkler fra maskiner og CI-jobber som installerte pakkene i tidsrommet, og bytt til
pull_requestframforpull_request_targeti egne arbeidsflyter.