
Preuve de concept et laboratoire pour une SSRF de webhook GitLab CE via la plage this-network 0.0.0.0/8, permettant à un Maintainer d'atteindre le loopback et de lire les réponses depuis les journaux de hook.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitlab-webhook-0net-ssrf
GitLab CE 19.4.1 - GitLab Inc.
Gitlab::HTTP_V2::UrlBlocker bloque exactement 0.0.0.0, :: et 127.0.0.0/8. Il ne bloque pas le reste de 0.0.0.0/8. Linux traite cette plage comme « ce réseau » et livrera 0.0.0.1 à la pile locale. Un Maintainer de projet peut créer un webhook sur http://0.0.0.1:<port>/ alors que allow_local_requests_from_web_hooks_and_services est désactivé. GitLab stocke 8 Ko de la réponse dans les logs de hook.
Un Maintainer de son propre projet peut faire en sorte que GitLab envoie une requête POST vers la loopback via 0.0.0.1 et récupérer le corps de la réponse depuis le log de hook.
| ID | pas encore de CVE |
| CWE | CWE-918 |
| CVSS | Élevé : 8.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N |
| Produit | GitLab CE |
| Affecté | 19.4.1-ce.0 (26212baacadb sur l'arbre v19.4.1-ee) |
| Auth | Maintainer authentifié (admin_web_hook) |
| Licence | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 uniquement |
POST /api/v4/projects/:id/hooks avec url=http://0.0.0.1:<port>/. Déclencher ou tester. Lire response_body depuis l'API des événements de hook. Sur un omnibus tout-en-un, Sidekiq partage le netns du conteneur avec Puma, Workhorse et les exporters. Un Maintainer de son projet ne devrait pas pouvoir lire ceux-ci.
http://127.0.0.1/ et http://169.254.169.254/ restent bloqués. Il ne s'agit pas d'IMDS via link-local. C'est le trou du « ce réseau ».
Même produit, résidus similaires : Gitea HTTPS import pin drop, Gitaly fetch omits resolved_address.
J'ai lu url_blocker.rb après les correctifs SSRF de la 19.4.1. validate_localhost correspond exactement à "::" et "0.0.0.0" plus Socket.ip_address_list. validate_loopback correspond à ipv4_loopback? (127.0.0.0/8) et à la loopback ipv6. Les specs couvrent les alias octaux/hexadécimaux de 127.0.0.1. Elles ne mentionnent jamais 0.0.0.1.
Le webhook est le réceptacle du corps : WebHookService envoie une requête POST et stocke 8 Ko. Le Maintainer dispose de admin_web_hook.
J'ai déployé l'image standard gitlab/gitlab-ce:19.4.1-ce.0. Requêtes locales désactivées. Un catcher à l'intérieur du netns GitLab sur 0.0.0.1:18080. Création de hook http://127.0.0.1:18080/ et http://169.254.169.254/ : 422 Invalid url given. Création de hook http://0.0.0.1:18080/ : 201. Test des événements push : 201. Le response_body des événements de hook contient GITLAB-WEBHOOK-0NET-SSRF-WITNESS.
Fausses pistes déjà consignées : /users/sign_in avec Accept: application/json renvoie 404 ; un mot de passe seed contenant root déclenche WeakPasswords ; ajouter 0.0.0.1 à lo le place dans Socket.ip_address_list, donc validate_localhost renvoie 422 sur le « positif » ; IP_FREEBIND plus ip route add local 0.0.0.1/32 dev lo effectue le bind sans rejoindre la liste d'adresses ; le $ dans le mot de passe compose nécessite $$. Un reverse shell. Du théâtre. L'oracle, c'est 201 sur 0.0.0.1, 422 sur 127.0.0.1, le témoin dans le log.
cd lab
./run.sh
Cible uniquement http://127.0.0.1:18420. GitLab CE nécessite plusieurs Go de RAM. Lab autorisé uniquement.
negative-a 127.0.0.1 blocked
negative-b link-local blocked
positive-hook id=1
SUCCESS GITLAB-WEBHOOK-0NET-SSRF webhook response_body from 0.0.0.1:18080 hook_id=1 GITLAB-WEBHOOK-0NET-SSRF-WITNESS
def validate_localhost(addrs_info)
local_ips = ["::", "0.0.0.0"]
local_ips.concat(Socket.ip_address_list.map(&:ip_address))
return if (local_ips & addrs_info.map(&:ip_address)).empty?
raise BlockedUrlError, "Requests to localhost are not allowed"
end
def validate_loopback(addrs_info)
return unless addrs_info.any? { |addr| addr.ipv4_loopback? || addr.ipv6_loopback? }
raise BlockedUrlError, "Requests to loopback addresses are not allowed"
end
Refuser 0.0.0.0/8 (et ::ffff:0.0.0.0/104) de la même manière que 127.0.0.0/8 est refusé.
url_blocker.rb · web_hook_service.rb · project_hooks.rbGNU Affero GPL v3.0. Voir LICENSE. Lab loopback uniquement. Aucune garantie.