Hackearon a GitHub. La amenaza real es TeamPCP.
El 19 de mayo de 2026 GitHub perdió 3.800 repositorios internos por una extensión de VS Code envenenada en el laptop de un empleado. El mismo grupo, TeamPCP, golpeó a Trivy en marzo y a TanStack en mayo. Qué es realmente nuevo en la campaña 2026 contra la cadena de suministro, y qué debe hacer tu equipo de ingeniería esta semana.
El 19 de mayo de 2026, la empresa que aloja el código del mundo fue hackeada. No por un zero-day en un balanceador de carga, no por una intrusión sofisticada de Estado-nación. Por una extensión de VS Code instalada por un empleado. Aproximadamente 3.800 repositorios internos exfiltrados. GitHub lo confirmó públicamente a las 6:25 AM PDT del 20 de mayo.
Si lo lees como un evento aislado, te estás perdiendo la historia mayor. El mismo grupo — TeamPCP, también rastreado como UNC6780 — ha estado ejecutando el mismo playbook contra el tooling de desarrollo todo el año. Trivy en marzo. CheckMarx KICS. LiteLLM. Telnyx SDK. TanStack el 11 de mayo. La infraestructura cloud de la Comisión Europea. Ahora GitHub mismo.
Esta es la primera campaña creíble de extremo a extremo que rompe las suposiciones sobre las que cada equipo de ingeniería construyó su postura de dependencias. La firma no ayudó. La provenance no ayudó. Los maintainers reputados no ayudaron. El CI amigable a auditorías no ayudó. Aquí qué cambió, por qué y qué hacer esta semana.
Los cuatro incidentes que conectan
Trivy (Aqua Security) — 19 marzo 2026. Un atacante explotó una rotación de credenciales incompleta tras una configuración previa errónea de pull_request_target. Con un solo PAT sobreviviente, tomaron control del service account aqua-bot y force-pushed 75 de 76 tags de versión en aquasecurity/trivy-action apuntando a commits maliciosos. Cualquier workflow pinneado a @v0.28.0 o cualquier tag empezó a ejecutar código del atacante sin que cambiara un solo archivo en el repositorio. El malware raspaba memoria del Runner buscando claves SSH, credenciales AWS/GCP/Azure, tokens de Kubernetes y contraseñas de bases de datos, las cifraba con AES-256-CBC + envoltura RSA-4096, y las exfiltraba a través de dominios typo-squatted y dead-drops en repos GitHub. Una puerta trasera persistente (sysmon.py como servicio systemd) en máquinas de desarrolladores usaba C2 sobre blockchain ICP. Un gusano separado — CanisterWorm — se auto-propagó a 47+ paquetes npm. (Palo Alto Networks)
TanStack — 11 mayo 2026, 19:20 UTC. En seis minutos, se publicaron 84 artefactos maliciosos en 42 paquetes @tanstack/*, incluyendo @tanstack/react-router (12.7M descargas semanales). Cadena del ataque: un fork disparó bundle-size.yml vía pull_request_target, ejecutando código controlado por el atacante en el contexto de confianza del repositorio base. Ese código envenenó el caché del pnpm store con una clave pre-computada, persistiendo ocho horas. Cuando el release.yml legítimo corrió en main, restauró el caché envenenado. Los binarios del atacante luego localizaron el proceso Runner.Worker de GitHub Actions vía /proc/*/cmdline y volcaron memoria desde /proc/<pid>/mem para extraer el token OIDC, publicando a npm directamente bypaseando el propio paso de publish del workflow. Este es el primer caso documentado de un paquete npm malicioso con provenance SLSA Build Level 3 válida. Sigstore verificó el build correctamente. Lo que SLSA no garantiza es que el código que se está construyendo sea seguro. (Postmortem TanStack, Snyk)
GitHub mismo — divulgado 20 mayo 2026. Una extensión de VS Code envenenada en el laptop de un empleado. Acceso total a todas las credenciales en ese dispositivo. 3.800 repositorios internos exfiltrados. GitHub aún no ha nombrado la extensión. TeamPCP reclamó responsabilidad y comenzó a vender los datos en foros de cibercrimen. GitHub rotó secretos críticos, aisló el endpoint y dice que no tiene evidencia de que datos de clientes fuera de repositorios internos hayan sido tocados — investigación en curso. (TechCrunch, SecurityWeek, Help Net Security)
Gusano Mini Shai-Hulud — corriendo a lo largo de la campaña. El payload auto-replicante de TeamPCP se propaga a través de credenciales CI/CD robadas, enumerando otros paquetes que cada maintainer comprometido puede publicar y republicándolos con lógica de inyección idéntica. Los vectores de persistencia incluyen hooks de Claude Code en .claude/settings.json, tasks de VS Code workspace en .vscode/tasks.json, servicios systemd en Linux y LaunchAgents en macOS — algunos de los cuales destruyen home directories si el token de GitHub es revocado posteriormente.
No son cuatro brechas. Es una campaña con un operador, golpeando cuatro fronteras de confianza.
Qué es realmente nuevo
El objetivo es la pipeline, no el producto. La mayoría de ataques previos a la cadena de suministro apuntaban a la dependencia (left-pad, event-stream, ua-parser-js). Estos apuntan a la automatización que construye, firma y publica la dependencia. El artefacto comprometido es idéntico en todo a un release legítimo porque fue un release legítimo — producido con las claves del maintainer, sobre la infraestructura del maintainer, con la attestation de provenance del maintainer. No hay nada contra qué verificar.
Memory scraping en runners CI es ahora el playbook por defecto. Los runners de GitHub Actions entregan un token OIDC de corta vida a los workflows para autenticación cloud. El token vive en memoria del proceso. Si puedes ejecutar cualquier código en el contexto de usuario del runner, puedes leerlo. El ataque a TanStack probó que esto funciona en producción, a escala, contra un maintainer que no estaba haciendo nada obviamente mal. La respuesta defensiva — runners efímeros con aislamiento estricto de procesos — no es lo que la mayoría de repos usan.
La provenance SLSA es una señal útil que ahora es activamente engañosa. Sigstore firma lo que el build produjo. No valida los inputs. Si el caché fue envenenado, el binario que se firma es malicioso — y la firma es matemáticamente válida. Provenance + firma sigue siendo necesario (te permite trazar qué build produjo qué artefacto) pero ya no es suficiente (no puedes inferir seguridad de una attestation válida sola). Cada tutorial de “verifica la firma” escrito antes de mayo 2026 necesita un asterisco.
La persistencia ahora es vía herramientas de desarrollo, no malware-como-malware. Extensiones de VS Code, hooks de Claude Code, el tasks.json del workspace, scripts postinstall de npm — todos ejecutan código en la máquina del desarrollador, con los permisos del desarrollador, cuando el desarrollador abre el repo. Son más difíciles de detectar que implants tradicionales porque se ven exactamente como las herramientas legítimas de productividad que imitan. El detalle del servicio systemd del worm de Trivy que limpia $HOME cuando el token de GitHub es revocado es pequeño pero revelador: el operador quiere que mantengas el token activo.
El blast radius se compone a través de ataques. Trivy fue comprometido → los secretos CI de usuarios de Trivy fueron robados → esos secretos incluían tokens de publish de npm → 47+ paquetes downstream fueron comprometidos → esos paquetes fueron instalados en miles de runs de CI → más secretos robados → más paquetes comprometidos. Mini Shai-Hulud automatizó la propagación. Para cuando pasó TanStack, el atacante tenía miles de tokens robados. Para cuando pasó GitHub, tenían un foothold en la cadena de suministro de la extensión VS Code de alguien. Cada salto exitoso alimentó al siguiente.
Qué se rompió de tus defensas
Si escribiste tu postura de seguridad de cadena de suministro en 2024 y no la has revisitado en 18 meses, esto es lo que ahora está obsoleto:
- “Pinneamos las dependencias a versiones.” Versiones son tags. Tags pueden ser force-pushed. El ataque a Trivy no cambió ni un solo archivo de workflow — cambió a dónde apuntaban tags de versión existentes. Necesitas pinnear a SHA de commit inmutables, no a tags.
- “Confiamos en paquetes firmados de maintainers reputados.” Los maintainers de TanStack son legítimos y los paquetes estaban firmados. La clave de firma era el token OIDC del runner, sacado de memoria. La reputación no es defensa contra un ataque a la pipeline del maintainer.
- “Tenemos SBOM y SLSA L3.” SLSA L3 produce un registro verificable de qué construyó la pipeline. No dice nada sobre si los inputs a esa pipeline eran confiables. Todavía necesitas verificación separada del código fuente que estás construyendo.
- “Nuestro CI es privado.” Los runners de GitHub Actions en repos privados no están aislados de forks públicos si usas
pull_request_target. El patrón Pwn Request es ahora tooling mainstream para atacantes — no una preocupación teórica. Audita cadapull_request_targeten cada workflow esta semana. - “Nuestros desarrolladores solo instalan extensiones de VS Code reputadas.” El marketplace de extensiones no está curado adversariamente. Microsoft valida la identidad del publisher y firma el binario de la extensión; no auditan el JavaScript que corre cuando instalas. Cualquier cosa que instales obtiene acceso total a tu shell, tus cookies, tu SSH agent y tus SDK de cloud.
Qué sí funciona (hazlo esta semana)
Pinnea cada GitHub Action externa a un SHA de commit. No @v4, no @latest. El SHA completo. Usa Dependabot para actualizar SHAs vía PRs que realmente revises. Costo de tiempo: un ingeniero por un día en un repo normal.
# malo — apunta a un tag que el atacante puede force-pushar
- uses: aquasecurity/[email protected]
# bueno — apunta a un commit inmutable
- uses: aquasecurity/trivy-action@f78e9ecf42a1a4af5e6d2d3f3e8a9e2d1b3c4d5e6
Elimina pull_request_target a menos que puedas cercarlo. Si lo necesitas para benchmarks o labels, restríngelo con if: github.event.pull_request.head.repo.full_name == github.repository y nunca hagas checkout del código del PR. Trata los workflows con pull_request_target como si corrieran con tu secreto más sensible, porque efectivamente lo hacen.
Migra a runners efímeros sin caché persistente. Los runners hosted por GitHub son efímeros por defecto — eso es bueno. Pero el caché se comparte entre workflows. Audita qué workflows usan actions/cache y qué paths cachean. El ataque a TanStack funcionó porque el pnpm store era un caché compartido entre branches. O no cachees nada cercano a secretos, o scopea las claves de caché por branch/workflow con aislamiento por hash.
Workload identity por repo, no a nivel de org. Si usas OIDC para autenticar desde GitHub Actions a AWS/GCP, la trust policy del lado cloud debe pinnear al repo específico y al workflow, no repo:org/*. La trust policy OIDC de TanStack era excesivamente permisiva — arreglarla habría hecho el token robado inútil.
Audita las extensiones en máquinas de desarrolladores. Esta semana, dumpea la lista de extensiones VS Code de cada máquina de ingeniero. Por cada extensión: quién es el publisher, cuándo se actualizó por última vez, pide permisos inusuales, ¿realmente la usas? Desinstala todo lo que no uses activamente. No hay extensión “pequeña” en una máquina de dev — todas corren JavaScript con tus privilegios de shell.
Rota cada secreto que haya tocado un runner CI. Si has usado Trivy, TanStack Router, o cualquier paquete downstream de ellos en CI en los últimos 90 días, asume que la memoria del runner fue legible. Rota tokens de publish de npm, bindings OIDC de cloud, secretos de repo, claves SSH de deploy. Es doloroso. También es mucho menos doloroso que la alternativa.
Verifica provenance — y audita el código fuente por separado. Sigstore + SLSA todavía importa. Pero agrega un paso que construya el paquete desde fuente en tu propio ambiente aislado y diferencia el resultado contra el artefacto publicado. Esto atrapa cache poisoning. Varias herramientas hacen esto hoy; Snyk, Aikido y StepSecurity todas tienen variantes.
Suscríbete a los advisories de seguridad de tus dependencias. GitHub Security Advisories, advisories de npm, la base de datos OSV de Open Source Security Foundation. La mayoría de estos ataques fueron públicos en una hora. La mayoría del impacto ocurre a equipos que se enteraron tres días después por un blog post.
Qué estamos haciendo por clientes esta semana
Cada cliente de Softronic con retainer de Ciberseguridad está recibiendo una auditoría de emergencia de cadena de suministro esta semana. Alcance:
- Inventario completo de GitHub Actions en workflows activos, con estado de pinning (SHA vs tag).
- Auditoría de
pull_request_target— cada workflow, cada guard, cada checkout. - Trust policies de workload identity en AWS/GCP — cada binding, estrechado a workflows específicos.
- Inventario de extensiones VS Code por máquina de ingeniero, con scoring de riesgo.
- Lista de rotación de secretos con orden de prioridad.
- Un runbook para qué hacer si una dependencia en tu árbol fue nombrada en cualquier advisory 2026 de TeamPCP.
Esto es 8–16 horas de trabajo por cliente, dependiendo del tamaño del repo. Está incluido en el alcance del retainer existente de $4K/mes, no es un upsell. Si no estás en retainer con nosotros y quieres este trabajo como engagement único, es un fee fijo de $3.500 por la auditoría completa, runbook y lista de remediación — turnaround 5 días hábiles.
Resumen de precios
- Auditoría de emergencia de cadena de suministro: fee fijo $3.500, 5 días hábiles, auditoría completa + runbook de remediación.
- Pentest: desde $8K para web app focalizada, $15-25K para app + cloud + red.
- Retainer de seguridad: desde $4.000/mes. Ahora incluye higiene de cadena de suministro como parte del alcance estándar.
Lo importante
Ya no puedes inferir la seguridad de una dependencia desde la reputación del maintainer, la validez de la firma, o la presencia de provenance. La campaña 2026 contra la cadena de suministro — TeamPCP, Mini Shai-Hulud, y lo que venga después — ha vuelto la pipeline de build misma la superficie de ataque. El trabajo de ingeniería para defenderla no es glamoroso: pin SHAs, cerca pull_request_target, scopea trust OIDC, audita tus herramientas. Lo hemos estado haciendo desde marzo. Si quieres ayuda para terminarlo esta semana, empieza un engagement. La discovery call es gratis.