- Solo con una falla en el protocolo interno de la ruta de
git pushera posible lograr ejecución remota de código en el backend; GitHub.com ya fue mitigado, pero GHES requiere aplicar el parche - La push option, una entrada controlada por el usuario, entraba tal cual en el encabezado
X-Stat, lo que permitía inyectar un campo nuevo con un solo punto y coma; además, se abusó del comportamiento de last-write-wins, donde el valor posterior de una misma clave sobrescribe al anterior - Al combinar los campos inyectables
rails_env,custom_hooks_diryrepo_pre_receive_hooks, era posible eludir el sandbox y ejecutar hooks desde una ruta elegida por el atacante con privilegios del usuariogit - El mismo mecanismo también permitió inyectar el flag de enterprise mode en GitHub.com, confirmando ejecución de código en un shared storage node, lo que además dejaba accesibles repositorios de otros usuarios y organizaciones en ese nodo
- Esto muestra cómo, en una arquitectura multiservicio donde distintos servicios confían en un formato compartido, la combinación de falta de sanitización de entrada, rutas de ejecución no productivas y ausencia de validación de rutas puede convertirse en una vulnerabilidad grave
Respuesta inmediata y alcance del impacto
- En GitHub.com este problema ya fue mitigado y no se requiere ninguna acción adicional
- GitHub Enterprise Server requiere atención inmediata, y debe actualizarse a GHES 3.19.3 o superior, que incluye la corrección de
CVE-2026-3854 - El rango de versiones vulnerables es GHES 3.19.1 o inferior; las versiones corregidas indicadas son
3.14.24,3.15.19,3.16.15,3.17.12,3.18.6,3.19.3 - Al momento de redactarse, el 88% de las instancias de GHES seguían siendo vulnerables
- La información técnica adicional y los procedimientos de recuperación de GitHub pueden consultarse en el blog de seguridad de GitHub
- Los clientes de Wiz pueden identificar instancias vulnerables de GHES con la consulta preconstruida del Wiz Threat Center
Contexto de la investigación y enfoque
- La infraestructura interna de git de GitHub procesa todas las rutas de
git push, y varios servicios internos están escritos en distintos lenguajes de programación - En este tipo de estructura multiservicio, las diferencias en cómo cada componente analiza y confía en los datos compartidos pueden derivar en vulnerabilidades
- Antes, extraer y auditar la gran cantidad de binarios compilados tipo caja negra que conforman este pipeline requería demasiado tiempo y trabajo manual
- Con herramientas potenciadas por IA y reverse engineering automatizado basado en
IDA MCP, fue posible analizar rápidamente los binarios compilados y reconstruir el protocolo interno - En ese proceso, se rastrearon de forma sistemática los puntos donde la entrada del usuario afectaba el comportamiento del servidor a lo largo de todo el pipeline, y se descubrió una falla fundamental en el flujo de entrada
Arquitectura interna y límites de confianza
- Cuando
git pushentra por SSH, la solicitud fluye porbabeld,gitauth,gitrpcdy luego el pre-receive hook babeldes el punto de entrada para todas las operaciones de git y recibe la conexión SSH, mientras que la autenticación se delega agitauthgitauthverifica las credenciales del usuario y los permisos de push sobre el repositorio, y devuelve políticas de seguridad como límites de tamaño de archivo o reglas para nombres de ramas- Con base en esa respuesta,
babeldconstruye el encabezado internoX-Statcon metadatos de seguridad gitrpcdrecibe el encabezadoX-Staty configura el entorno de los procesos posteriores, confiando por completo enbabeldsin autenticación propia- El pre-receive hook verifica límites de tamaño de archivo, reglas de nombres de ramas, integridad de LFS y hooks personalizados definidos por administradores antes de aceptar el push
- La pieza clave era el encabezado X-Stat, que contenía pares
key=valueseparados por; - Los servicios internos dividían
X-Statpor;y llenaban un mapa; si una misma clave aparecía dos veces, el valor posterior sobrescribía al anterior según la regla de last-write-wins babeldtambién añadía alX-Statlas push options pasadas congit push -o, en campos comopush_option_0,push_option_1ypush_option_count
Causa de la vulnerabilidad: inyección de campos en X-Stat
babeldcopiaba al encabezadoX-Statvalores de push options controlados por el usuario sin sanitizar los puntos y coma- Como
;era el separador de campos enX-Stat, un solo punto y coma dentro de una push option permitía salir del campo original y crear un nuevo campo controlado por el atacante - Por ejemplo, al inyectar
large_blob_rejection_enabled=bool:falsedentro depush_option_0, el valor posterior sobrescribía elbool:trueestablecido previamente - Este comportamiento se confirmó tanto mediante análisis de binarios como en capturas de paquetes de instancias reales de GHES
- Al combinar reverse engineering con análisis a nivel de wire, se mapearon los campos inyectables de
X-Stat - En particular, se identificaron como sensibles para la seguridad los campos
rails_env,custom_hooks_dir,repo_pre_receive_hooks,large_blob_rejection_enabled,reject_sha_like_refsyuser_operator_mode - Entre ellos,
rails_env,custom_hooks_diryrepo_pre_receive_hookseran las tres piezas clave que llevaban a la ejecución remota de código
Ruta hacia RCE en GHES
- GHES admite custom pre-receive hooks que se ejecutan antes de aceptar un push
- El binario de pre-receive tenía dos rutas de ejecución diferenciadas únicamente por el valor
rails_envdeX-Stat - Si el valor era
production, ejecutaba los hooks dentro de un sandbox; con cualquier otro valor, los ejecutaba directamente con los privilegios del usuario de serviciogit, sin sandbox ni aislamiento - Por eso, al inyectar un valor no productivo en
rails_env, era posible evadir el sandbox - Luego, al inyectar
custom_hooks_dir, el atacante podía controlar el directorio base donde se buscaban los scripts de hooks - Finalmente, al inyectar en
repo_pre_receive_hooksuna definición de hook con path traversal, la resolución de rutas del binario combinaba el directorio controlado por el atacante con la carga de traversal y terminaba apuntando a una ruta arbitraria del sistema de archivos - La ruta de ejecución no productiva ejecutaba directamente esa ruta resuelta, sin argumentos, sin sandbox y como usuario de servicio
git - En una validación real, un solo
git pushdevolvió la salidauid=500(git), confirmando RCE con privilegios del usuariogit - Con esos privilegios era posible obtener control total sobre la instancia de GHES, incluida lectura y escritura del sistema de archivos y visibilidad sobre la configuración de servicios internos
Escalamiento a GitHub.com y exposición entre tenants
- Al aplicar la misma cadena de explotación a un repositorio de GitHub.com, al principio el push tuvo éxito pero los custom hooks no se ejecutaron
- Al inyectar
user_operator_mode=bool:truey comparar la salida de depuración en ambas plataformas, se vio que en GitHub.com no se alcanzaba la ruta de código de custom hooks - Más reverse engineering confirmó la existencia en
X-Statde un flag booleano que controlaba si el servidor operaba en enterprise mode - En GHES, este flag era
truepor defecto, por lo que la ruta de custom hooks siempre estaba activa; en GitHub.com, el valor predeterminado erafalse, así que en condiciones normales esa ruta no se alcanzaba - Como este flag también podía inyectarse con el mismo mecanismo, bastaba con añadir un campo más para que toda la cadena de explotación funcionara también en GitHub.com
- Después, se recibió desde la infraestructura interna de GitHub.com el resultado de ejecutar
hostname, confirmando RCE en GitHub.com - GitHub.com es una plataforma multitenant, por lo que los repositorios de múltiples usuarios y organizaciones se almacenan en infraestructura backend compartida
- La ejecución de código ocurrió en un shared storage node, donde el usuario
gittiene amplios permisos de sistema de archivos para procesar todas las operaciones de repositorios de ese nodo - Si ese usuario se ve comprometido, también pueden leerse repositorios de otras organizaciones y usuarios presentes en el nodo, sin importar su propietario
- Al enumerar las entradas del índice de repositorios accesibles desde dos nodos comprometidos, cada uno contenía millones de entradas, incluyendo repositorios de otros usuarios y organizaciones
- No se accedió al contenido real de repositorios de otros tenants; solo se usaron cuentas de prueba propias para verificar que los permisos de sistema de archivos del usuario
gitpermitían leer todos los repositorios del nodo
Lecciones clave y cronograma de divulgación
- Con un solo
git push, era posible explotar una falla del protocolo interno y obtener ejecución remota de código en la infraestructura backend - Cuando varios servicios escritos en distintos lenguajes intercambian datos mediante un protocolo interno compartido, las suposiciones de confianza de cada servicio se convierten en superficie de ataque
- En esta cadena, un servicio insertaba sin cambios el valor de una push option, otro confiaba en todos los campos de
X-Stat, y el pre-receive hook asumía que en producciónrails_envsiempre seríaproduction - Las rutas de código no productivas dentro de binarios de producción, la ausencia de validación contra path traversal en scripts de hooks y la falta de sanitización de entrada en protocolos basados en delimitadores son patrones que pueden aparecer en otros codebases
- Los equipos que operan arquitecturas multiservicio deben revisar especialmente cómo fluyen las entradas controladas por el usuario a través de protocolos internos, sobre todo cuando configuraciones sensibles de seguridad derivan de formatos de datos compartidos
- En esta investigación, herramientas de reverse engineering asistido por IA, incluido
IDA MCP, permitieron acelerar el análisis de binarios compilados y la reconstrucción de protocolos internos - A medida que estas herramientas maduren, probablemente tendrán un papel cada vez más importante para encontrar esta clase de vulnerabilidades que requieren análisis profundos entre componentes
- Según el cronograma de divulgación, la vulnerabilidad de inyección de push option en X-Stat se descubrió el
2026-03-04; ese mismo día se confirmó el RCE enGHES 3.19.1, se reportó a GitHub y también se desplegó la corrección en GitHub.com - El
2026-03-10se asignaron CVE-2026-3854 yCVSS 8.7, y se publicó el parche para GHES - El
2026-04-28se hizo pública
1 comentarios
Comentarios de Hacker News
Básicamente hicieron que en los headers de seguridad críticos que configura el servicio interno de autenticación también entrara una cadena arbitraria puesta por el usuario final con
git push -oes fácil decirlo después, pero aun así esto resulta demasiado absurdo
El enfoque de reversing asistido por IA muestra muy bien las fortalezas actuales de los agentes LLM
como son modelos entrenados intensamente con código, pueden acelerar muchísimo la comprensión de sistemas internos complejos
la investigación de seguridad normalmente combina 1) entender comportamientos internos complejos y 2) encontrar vulnerabilidades dentro de eso,
y muchas veces, una vez que se revela el mecanismo interno real, la vulnerabilidad en sí puede volverse sorprendentemente obvia
CVE-2026-3854 no era un caso inmediatamente obvio incluso conociendo el interior,
pero si este command injection hubiera estado expuesto en una superficie de ataque más tradicional o más accesible, probablemente lo habrían encontrado muy rápido
pero últimamente da la impresión de que esa tendencia se ha desordenado un poco, o incluso de que algunos la obstaculizan a propósito para proteger el lock-in de dev/vendor que nace de la complejidad sintáctica de C++
Casi parece que hay gente de Wiz trabajando ahí, porque el resultado se ve bastante bueno
el producto, pese a un crecimiento extremo y a la inflación de funcionalidades, todavía aguanta bastante bien,
y el equipo de seguridad encuentra cosas realmente interesantes con frecuencia
osv-scannerytrivypara ver únicamente lo críticopero al mismo tiempo se queda callado ante tareas bastante sospechosas como consultar el DC por CLI y resetear credenciales, lo cual resulta decepcionante
Cuando
babeldreenvía la solicitud de push, mete las push options en el header X-Stat de la solicitud interna,y ese valor es una cadena arbitraria puesta por el usuario con
git push -opero copiaron el valor tal cual sin sanitizar los punto y coma,
y como
;es el separador de campos en X-Stat, eso permite escapar del campo original y que el atacante cree campos nuevosde verdad es el tipo de error más simple posible, tan al alcance que parece fruta tan baja que ya quedó enterrada bajo tierra
Incluso siendo una vulnerabilidad que encontraron antes de que se explotara,
no sé si hacía falta meter miedo innecesario con expresiones como BREAKING, unauthorized access y millions of repositories
https://x.com/wiz_io/status/2049153209982140718
GitHub tuvo suerte de que quien se topó con esto fuera el fuzzing de Wiz y no un atacante patrocinado por un Estado
Que el 88% de las instancias de GHES todavía no haya aplicado un parche crítico de seguridad publicado hace 7 semanas se ve bastante serio
https://docs.github.com/en/enterprise-server@3.19/admin/release-notes#3.19.3
aplicar incluso una release de nivel parche requiere horas de downtime,
y ni siquiera hay una forma de upgrade HA soportada, así que hasta a los clientes responsables les cuesta seguir de inmediato la última versión
cuando te quejas, todos te dicen que te pases a GitHub Enterprise Cloud,
pero en tiempos como estos también es dudoso cuánta gente elegiría eso con confianza
aun así, GHES al menos tiene la ventaja de que no se cae con las interrupciones diarias de github.com
y parece que intentan programar el upgrade para una fecha con poco impacto operativo
aun así, si es una instancia pública, debería actualizarse de inmediato
solo con la información del artículo y el código fuente público de GitHub Enterprise no parece difícil idear una reproducción
o puedes seguir el calendario y esperar que no pase nada; por lo general se elige lo segundo
no sería raro que un producto on-prem se actualice una sola vez al año
en software enterprise con mucho volumen de datos, era común que cualquier detalle menor rompiera la instalación y obligara al equipo operativo a hacer rollback
los upgrades de SharePoint de antes casi se sentían como tirar los dados
Esto también es un gran logro de Wiz,
y se siente como un punto de inflexión que muestra cuánto empujan las herramientas de IA al RE y al descubrimiento de rutas de compromiso
es otro dato más de que la seguridad no debe depender de security through obscurity
Todos hablan de reemplazar GitHub, pero entonces queda la pregunta de qué usar
si incluso un lugar del tamaño de GitHub termina teniendo RCE, no es fácil asegurar que otras alternativas sean mejores
https://news.ycombinator.com/item?id=46961345
https://news.ycombinator.com/item?id=47712656
y usar GitHub solo como mirror mientras se aprovecha el CI gratuito
los secretos se pueden dejar en un proveedor de secret-hosting aparte
todavía me cuesta creer que Forgejo responda así de rápido y que GitHub se haya vuelto así de lento
los internos van en una instancia privada de Forgejo, y los públicos se suben a GitHub pero se espejan en Forgejo
me sorprendió que Forgejo fuera prácticamente un binario único y tan fácil de configurar,
y como todos los servicios internos apuntan a Forgejo, habría muy poca fricción si hubiera que dejar GitHub
con la imagen Docker todo-en-uno y unos cuantos GitLab runner alcanza para equipos pequeños o medianos,
y no hay razón para complicarse hasta la versión en Kubernetes salvo que realmente haga falta
Ya era impresionante que la IA encontrara vulnerabilidades en código fuente,
pero lograrlo incluso en ejecutables binarios de verdad sorprende mucho
tiene un potencial enorme tanto para bien como para mal
y una vez más queda la lección de que no hay que tratar los datos como comandos
toda entrada de usuario debe sanitizarse
así que que sea fuerte en source-to-source o text-to-source ya era algo conocido,
y que también encaje bien para entender versiones en asm quizá no sea algo totalmente inesperado
aun así, sigue siendo impresionante
Me pregunto si realmente será posible determinar si esto fue explotado
con logs del protocolo HTTP/git probablemente se pueda comprobar hasta cierto punto si hubo explotación,
pero es posible que no haya quedado registro de a qué se accedió realmente ni de quién lo hizo
si el exploit podía ejecutarse de forma independiente en el servidor git, por definición podría esquivar el logging