Tornar al Blog

ChainDrop: 444 paquets de npm compromesos i ni un sol pedaç per aplicar

No és una fallada del codi. És un compte robat.
ChainDrop · 444 paquets · 4 d'agost del 2026

Portem l'estiu discutint si els agents autònoms serveixen per a alguna cosa. El 4 d'agost, entre les 09:35 i les 13:20 UTC, un programa sense ni una línia d'intel·ligència va fer tot sol, sense supervisió i en menys de quatre hores, una cosa que cap pilot corporatiu no ha aconseguit: va comprometre 444 paquets de npm, es va propagar amb les credencials que anava robant pel camí i, de passada, es va endur les claus de les eines d'IA dels desenvolupadors que va infectar. Es diu ChainDrop. No és un agent: és un cuc. I el que importa per a la teva empresa és que aquí no hi ha cap pedaç per aplicar.

El compte de GitHub del mantenidor de keyv —una llibreria d'emmagatzematge clau-valor que gairebé ningú no instal·la expressament, perquè hi entra com a dependència d'una dependència— va ser compromès. Amb aquest accés, l'atacant va empènyer codi maliciós a la branca principal i va deixar que el pipeline de publicació legítim fes la resta. A les 09:35 UTC sortia [email protected]. A partir de les 09:38 i fins a les 13:20, una segona onada completament automàtica va arribar a 433 paquets més fent servir les credencials que la primera havia collit.

Les xifres convé donar-les amb el seu matís, perquè no tothom compta el mateix. En el nombre de paquets hi ha acord: 444. En el de versions no: StepSecurity en comptabilitza 2.212 i Aikido 1.381, segurament perquè cadascú va tallar el recompte en un moment diferent d'una campanya que continuava movent-se. Microsoft ho descriu amb prudència com «més de 400 paquets d'editors no relacionats entre si». Els tres paquets de més abast, segons StepSecurity, són keyv (153,7 milions de descàrregues setmanals), flat-cache (149,9) i file-entry-cache (147,6). Aikido suma més de 2.000 milions d'instal·lacions mensuals en el conjunt. Ningú no ha comptat quantes empreses hi ha darrere d'aquestes instal·lacions; el que mesura aquest número és el calibre de la canonada per on es va colar.

La signatura era vàlida. I deia la veritat.

Aquí hi ha el detall que a nosaltres ens sembla el més important de tot l'episodi, i el que menys s'ha explicat. [email protected] es va publicar mitjançant trusted publishing amb OIDC i va sortir amb procedència vàlida: l'atestació criptogràfica que certifica des de quin repositori i amb quin workflow es va construir aquell artefacte. Ningú no va falsificar res: el pipeline oficial va compilar, signar i publicar codi que ja venia trojanitzat des del repositori.

Socket ho formula en una frase que val la pena copiar a la pissarra: la procedència atesta la integritat de la construcció, no la del codi font. És la diferència entre el segell del notari i el contingut del contracte. El notari certifica que vas signar tu, al seu despatx, aquell dia. No certifica que sabessis què estaves signant. La procedència respon «ho va construir el workflow oficial a partir d'aquell commit?» —sí— i no respon «aquell commit el va escriure qui tocava?».

La conseqüència pràctica és incòmoda i cal dir-la: si la teva política de dependències és «només instal·lem paquets amb procedència verificada», el 4 d'agost aquesta política va donar llum verda. No passa res per reconèixer-ho; la procedència apuja el llistó i cal continuar exigint-la. Però no és un cinturó de seguretat, i vendre-la com si ho fos —cosa que fa força material comercial— deixa molta gent tranquil·la pel motiu equivocat.

El codi corre abans que tu decideixis res

Cada paquet compromès va rebre dos fitxers —setup.mjs, el carregador, i Math_Symbol.js, de 727.680 bytes, que als paquets assolits per la segona onada apareix com math_init.js— i una línia al package.json: "preinstall": "node setup.mjs". Aquesta línia és tot l'atac. npm executa els scripts de preinstall abans d'acabar la instal·lació: abans dels teus tests, abans de la teva anàlisi de dependències, abans que ningú miri un diff. Al portàtil del desenvolupador i al runner d'integració contínua, exactament igual.

La mitigació cap en un paràmetre: npm ci --ignore-scripts. I com que no ens agrada vendre dreceres, el matís: no és gratis. Els paquets amb extensions natives que compilen durant la instal·lació deixen de funcionar, així que cal identificar-los i permetre'ls un a un. És feina d'una sola vegada i no torna, i és la diferència entre «m'he portat codi maliciós al disc» i «l'he executat amb el meu usuari».

Es va descarregar Bun de GitHub, i per això el teu proxy no va dir res

El carregador no portava intèrpret propi: es descarregava Bun v1.3.13 legítim des de github.com/oven-sh/bun/releases, amb l'artefacte correcte per a cada plataforma (Linux x64 amb glibc i amb musl, Linux arm64, macOS i Windows). És la tècnica que es coneix com viure de la terra, aplicada a la telemetria: l'indicador de xarxa que queda als teus logs apunta a github.com, un domini que ningú no bloqueja i que en una màquina de desenvolupament té trànsit durant tota la jornada.

Si la teva detecció es recolza en «connexions a dominis estranys», aquí no sona res. El que sí que és anòmal i sí que es pot buscar: un procés bun executant-se en un runner d'un projecte que mai no ha fet servir Bun, i fitxers nous amb nom d'utilitat matemàtica dins de node_modules. El senyal existeix, i cal anar a buscar-lo al comportament del procés.

El que es va endur, i la línia que gairebé ningú no té apuntada

L'escombrada va ser ampla, i cada casa la mesura diferent: StepSecurity parla d'unes 140 rutes de credencials, Aikido d'uns 200 patrons de cerca i Elastic de més de 300 patrons únics. Metodologies diferents, mateixa conclusió: no buscava una cosa concreta, buscava tot. Testimonis de npm i de la CLI de GitHub, credencials d'AWS —inclosos Secrets Manager i SSM en setze regions—, de Google Cloud, d'Azure, testimonis de HashiCorp Vault, comptes de servei de Kubernetes i claus SSH.

I després hi ha la línia nova: .claude/credentials.json, .cursor/credentials.json, .openai/auth.json, .anthropic/auth.json. Fes la prova a casa: obre el procediment de rotació de secrets de la teva empresa i busca aquestes quatre rutes. No hi són. Aquests comptes se solen donar d'alta sense passar per cap procés, acaben pagant-se amb la targeta d'algú de l'equip i rarament entren a l'inventari. Una clau d'API d'un proveïdor d'IA és diners directes —es consumeix i es factura— i, a mesura que aquestes eines es connecten a repositoris i a documentació interna, cada cop és també una porta a context de l'empresa. És l'evolució natural del malware que ja apunta als desenvolupadors i als seus agents en lloc del servidor de sempre.

El detall més desagradable és el que apunta StepSecurity sobre els runners de GitHub Actions: el payload llegeix la memòria del procés a través de /proc/<pid>/mem buscant la cadena "isSecret":true. Traduït: els secrets que la plataforma emmascara amb asteriscs al log són en clar a la memòria del procés que els fa servir. L'emmascarament protegeix del log, no de qui ja és dins de la màquina. És la mateixa lliçó que trèiem del servidor de CI/CD que desa les claus de producció, ara aplicada al runner efímer que la majoria considera prescindible precisament perquè dura cinc minuts.

El centre de comandament era a Ethereum

El cuc no portava l'adreça del seu servidor escrita a dins. La consultava: una crida eth_call a un contracte de la xarxa principal d'Ethereum, 0xE1f2395ee43e45A1556EC6438a88c31B83493103, llançada contra 75 punts d'accés RPC públics fins que algun contestés. El domini d'exfiltració observat va ser npm-cache.com; Elastic documenta a més awqhnjewqjkl[.]icu. Si el contracte no responia, el cuc queia a buscar la infraestructura de reserva en commits de GitHub. I Microsoft descriu un pla C sorollós: crear un repositori públic amb la descripció «Shai-Hulud: Here We Go Again», la picada d'ullet a la família de cucs de la qual aquest descendeix.

Bloquejar npm-cache.com al proxy està bé i costa un minut. Però convé entendre què s'aconsegueix: el domini és la part prescindible. L'adreça de destinació es llegeix d'una cadena de blocs pública que ningú no pot retirar, comissar ni donar de baixa, i l'atacant l'actualitza quan vol amb una transacció. Les llistes negres de dominis continuen servint per al que serveixen, que és força; simplement no tanquen aquesta porta.

El parany: revocar el testimoni és el que el dispara

Si de tot l'article et quedes amb un paràgraf, que sigui aquest. El payload deixa instal·lat un vigilant: ~/.local/bin/gh-token-monitor.sh, arrencat per un servei de systemd a Linux o per un LaunchAgent a macOS. Consulta api.github.com/user cada 60 segons durant 24 hores. Quan el testimoni robat deixa de funcionar —és a dir, en l'instant exacte en què tu el revoques— executa una càrrega de l'atacant. Microsoft ho descriu com un component que «manté l'accés a credencials i conté un manejador destructiu si el testimoni vigilat es revoca».

Això inverteix el reflex correcte, que és el que ho fa perillós. Qualsevol amb formació decent, en assabentar-se que li han robat un testimoni, entra a GitHub i el revoca. És el primer que ensenyem. Aquí, aquell clic és el senyal de sortida. L'ordre que recomana StepSecurity, i que ens sembla l'únic raonable, inverteix els dos passos: primer es busca i s'elimina el vigilant —el fitxer, la seva unitat de systemd d'usuari, el seu LaunchAgent— i després es rota, des d'un equip net. Als runners també, encara que la intuïció digui que allà no cal perquè es destrueixen sols.

Aquest és el detall que converteix l'episodi de «ensurt de la cadena de subministrament» en «cal un procediment escrit». Improvisar la resposta a les onze de la nit, amb l'equip de desenvolupament mirant i algú preguntant cada deu minuts si ja està, és exactament l'escenari en què es prem el botó equivocat.

Aquí no hi ha res per apedaçar

Fa una setmana escrivíem sobre una biblioteca que ningú no va triar i que va obrir un forat en aplicacions Rails. Aquell cas tenia CVE, avís oficial, rang de versions afectades i llista de versions corregides. Podies creuar una llista amb l'altra, treure el nombre d'equips dins del rang i planificar una finestra. Era desagradable, però era un problema amb forma coneguda.

Això no. No hi ha fallada de codi: hi ha un compte robat. No existeix un [email protected] que arregli el que va fer el 6.0.0, perquè el 6.0.0 portava a dins un passatger amb bitllet en regla. El que hi ha és tornar a una versió anterior —o pujar a la neta allà on el mantenidor ha republicat, cosa que ha passat en uns quants dels 444— i assumir que qualsevol màquina que va executar la instal·lació ja ha lliurat els seus secrets. La pregunta operativa no és «estic apedaçat?»; és «quines màquines van fer npm install des de les 09:35 UTC del 4 d'agost, i quines credencials hi havia a cadascuna?».

L'ordre en què ho faríem nosaltres

  • Esbrina si t'ha tocat, mirant fitxers i no memòria. Als lockfiles (package-lock.json, pnpm-lock.yaml, yarn.lock), quines versions de la família keyv, cacheable, flat-cache i file-entry-cache hi van entrar a partir del 4 d'agost. Al disc, els fitxers setup.mjs, Math_Symbol.js i math_init.js dins de node_modules, i qualsevol package.json amb "preinstall": "node setup.mjs".
  • Abans de tocar ni un sol testimoni: treu el vigilant. ~/.local/bin/gh-token-monitor.sh, la seva unitat de systemd d'usuari i, a macOS, el seu LaunchAgent. Als portàtils i als runners. Aquest pas va primer; no és negociable.
  • Torna a versions segures amb --ignore-scripts. Reinstal·lar sense aquest paràmetre en una màquina amb el lockfile encara enverinat torna a executar el carregador.
  • Rota des d'un equip net, mai des de l'infectat: testimonis de npm, PAT i claus SSH de GitHub, credencials de núvol, comptes de servei de Kubernetes, testimonis de Vault. I la línia que falta a gairebé totes les llistes: les claus d'API de les eines d'IA.
  • Audita l'organització de GitHub. Workflows que ningú no recorda haver creat —StepSecurity en cita un anomenat «Run Copilot»—, commits atribuïts a [email protected], repositoris públics nous i canvis a .claude/settings.json o .vscode/tasks.json, que és on el cuc s'enganxava per tornar a arrencar.
  • Creua els logs d'auditoria de núvol amb aquelles credencials, del 4 d'agost fins avui, buscant ús des d'adreces que no són les teves. Rotar sense mirar això et diu que has tancat la porta, no què va passar mentre era oberta.
  • I el permanent: --ignore-scripts per defecte amb una llista curta d'excepcions justificades; un període de repòs abans d'adoptar una versió acabada de publicar, que en aquest cas hauria bastat; doble factor a npm; i testimonis efímers al CI en lloc de PAT de llarga vida desats al disc.

El que això no és

No és un argument contra npm ni contra el codi obert, i qui l'utilitzi com a tal aquesta setmana està fent trampa. La teva aplicació propietària arrossega també centenars de dependències; la diferència és que no les veus i que, quan això li passa a un proveïdor tancat, te n'assabentes sis mesos després per una carta. Aquí la cadena sencera és pública: per això hi ha cinc anàlisis forenses independents publicades en pocs dies i per això pots buscar els indicadors tu mateix aquesta tarda.

I «auditar totes les dependències» tampoc no és un pla. És el que es diu a la reunió i el que ningú no fa el dimarts següent: l'arbre de dependències d'un projecte mitjà no se'l llegeix ningú, ni aquest mes ni el que ve. El que sí que se sosté en el temps són dues rutines poc lluïdes: credencials que caduquen soles i credencials que arriben a poc. Un testimoni de npm que expira en una hora i només pot publicar un paquet val ben poc robat. I si el portàtil que compila pot desplegar a producció amb una clau desada al disc, el radi d'acció d'una línia preinstall és tota la teva infraestructura, que és una frase que fa por precisament perquè és literal.

Però el que ens endurem d'aquest episodi és el del principi. El 4 d'agost el control va funcionar exactament com està dissenyat: l'atestació es va generar, es va verificar i era correcta. I el paquet anava enverinat igualment. Això no s'arregla afegint una altra casella a la llista de comprovació. S'arregla sabent quina pregunta respon exactament cada control que tens posat —la procedència respon a «ho va construir qui diu?», i aquí s'acaba la seva feina— i quin dels teus riscos es queda sense ningú que el contesti. Qui va escriure el commit continua sent un assumpte de comptes, contrasenyes i segons factors a l'ordinador d'una altra persona. És la part avorrida, i és la que va decidir el 4 d'agost.

Nosaltres no venem eines de seguretat de la cadena de subministrament ni tenim cap producte que ho resolgui, i qui et digui que el té t'està venent una alarma per a una porta que no és la que es va obrir. El que sí que fem és la feina del voltant: saber quines aplicacions té una empresa, qui les construeix, quines credencials viuen al seu pipeline, quant duren i fins on arriben. És el que hi ha darrere dels nostres projectes de dades i aplicacions i de la consultoria que fem sense vendre llicències de ningú. Si avui no pots contestar quines màquines de la teva empresa van executar npm install la setmana passada, escriu-nos i ho mirem amb tu.

Nota sobre fonts. La cronologia ([email protected] publicat a les 09:35 UTC del 4 d'agost del 2026 mitjançant trusted publishing amb OIDC i procedència vàlida; segona onada de 09:38 a 13:20 UTC), el recompte de 444 paquets i 2.212 versions, les mides de setup.mjs i Math_Symbol.js (727.680 bytes), la descàrrega de Bun v1.3.13, les ~140 rutes de credencials, la lectura de /proc/<pid>/mem buscant "isSecret":true, el contracte d'Ethereum 0xE1f2395ee43e45A1556EC6438a88c31B83493103 amb 75 endpoints RPC, el vigilant gh-token-monitor.sh amb sondeig cada 60 segons durant 24 hores i l'ordre de remediació, de l'anàlisi de StepSecurity. La descripció del cuc, el manejador destructiu en revocar-se el testimoni vigilat, el xifratge AES-256-GCM de l'exfiltració i el repositori públic de reserva, de l'anàlisi de Microsoft Security del 4 d'agost del 2026. El compromís del compte de GitHub del mantenidor, el recompte alternatiu de 1.381 versions i els més de 2.000 milions d'instal·lacions mensuals, d'Aikido. La frase sobre que la procedència atesta la integritat de la construcció i no la del codi font, de Socket. Els indicadors addicionals i els més de 300 patrons de credencials, d'Elastic Security Labs. On les xifres no coincideixen entre fonts ho diem al text en lloc de triar la més alta. La lectura sobre la procedència com a segell i no com a cinturó, l'ordre de les tasques i la conclusió sobre vida i abast de les credencials és nostra. Imatge de portada: «Eimskip container terminal at Port of Reykjavík», de Quintin Soloviev, via Wikimedia Commons, sota llicència CC BY 4.0 (retallada).

Compartir:

Subscriu-te al nostre butlletí

Per rebre històries del món IT, novetats d'everyWAN i ofertes exclusives per a subscriptors, dona't d'alta a la nostra llista de correu

everyWAN
everyWAN