
GitLab - Alto - SSRF nell'importazione HTTPS di Gitea - Rimozione del pin di UrlBlocker
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitlab-gitea-import-ssrf
GitLab CE 19.4.1 - GitLab Inc.
L'importazione Gitea chiama UrlBlocker.validate! una volta, poi per https scarta l'URI con IP bloccato e passa a Faraday il nome host originale. Octokit UrlValidation convalida di nuovo e non riscrive env[:url]. Esegui il rebind del nome tra queste due ricerche e GitLab esegue GET su loopback come se fosse Gitea. Il JSON della lista dei repository torna su GET /import/gitea/status.json. Le importazioni HTTP mantengono il pin. L'accesso diretto a https://127.0.0.1 è bloccato.
Un utente autenticato che può aprire l'importazione Gitea sul proprio namespace personale può far sì che GitLab recuperi un host di cui ha eseguito il rebind verso loopback, e ne ottiene il corpo.
| ID | nessuna CVE al momento |
| CWE | CWE-918, CWE-367 |
| CVSS | Alto: 7.7 CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N |
| Prodotto | GitLab CE |
| Affetto | 19.4.1-ce.0 (26212baacadb sull'albero v19.4.1-ee) |
| Auth | utente autenticato, importazione Gitea, namespace personale |
| Licenza | GNU Affero GPL v3.0 |
| Lab | solo 127.0.0.1 |
Imposta l'URL host di Gitea su https://<nome-che-controlla>/. UrlBlocker consente il primo record A pubblico. client_options HTTPS mantiene quel nome host. Esegui il rebind verso 127.0.0.1. Faraday esegue GET sul catcher sul loopback del worker GitLab. provider_repos in status.json è qualunque cosa abbia restituito il catcher.
Su un omnibus all-in-one si tratta di Workhorse, Puma, exporter e qualsiasi altra cosa in ascolto sul worker. Non è RCE sull'host. Non è non autenticato. L'URL host HTTP rimane sull'IP bloccato.
Stesso prodotto, residui affini: Gitaly fetch omette resolved_address, webhook 0.0.0.0/8.
Ho letto prima le note di patch del 23 settembre 2026 per 19.4.1 / 19.3.3 / 19.2.7, poi l'albero v19.4.1-ee. RCE da regex in CI, XSS nel diff delle MR, lettura di file.path di Workhorse sono chiusi su questo pin. L'SSRF nell'importazione non era stato completato.
Import::GiteaController#client_options è il residuo:
host: verified_url.scheme == 'https' ? provider_url : verified_url.to_s,
HTTP usa verified_url (IP). HTTPS usa provider_url (il nome digitato dall'utente). LegacyGithubImport::Client costruisce l'api_endpoint di Octokit da quell'host. Gitlab::Octokit::UrlValidation chiama validate! e poi @app.call(env) senza riscrittura.
Ho avviato l'immagine stock gitlab/gitlab-ce:19.4.1-ce.0. Local-requests disattivato. CA del lab in /etc/gitlab/trusted-certs perché ssl.verify di Faraday è true. DNS per gitea.lab: le ricerche 1-2 restituiscono 1.1.1.1, le ricerche successive restituiscono 127.0.0.1. Il catcher condivide il netns di GitLab su :443. GET /import/gitea/status.json ha restituito provider_repos con nome GITLAB-GITEA-IMPORT-SSRF-WITNESS. Log del catcher: GET /api/v1/user/repos peer=127.0.0.1 host=gitea.lab.
https://127.0.0.1/ diretto: "Specified URL cannot be used". http://gitea.lab/ HTTP è rimasto sul pin e non ha restituito il witness.
Vicoli ciechi già registrati: /users/sign_in con Accept: application/json è 404 (l'HTML è 200); la password seed CveLabRoot9! è stata rifiutata perché contiene root; il bind-mount della CA in ro è EROFS dopo che la riconfigurazione collega il file; primi-due-poi-loopback alla ricerca 2 fa sì che UrlValidation blocchi prima che Faraday si connetta. La modalità pairs (dispari pubblico, pari loopback) è quella che ha funzionato. Una reverse shell. Teatro. L'oracolo è il witness in status.json più il peer del catcher.
cd lab
./run.sh
Target solo http://127.0.0.1:18400. GitLab CE richiede diversi GB di RAM. Il primo avvio è lento. Solo lab autorizzato.
status.json status=200 ... provider_repos ... GITLAB-GITEA-IMPORT-SSRF-WITNESS
catcher GET /api/v1/user/repos peer=127.0.0.1 host=gitea.lab status=200 witness=True
negative-a https://127.0.0.1 blocked
negative-b http://gitea.lab no-witness
SUCCESS GITLAB-GITEA-IMPORT-SSRF status.json mode=pairs http=200 GITLAB-GITEA-IMPORT-SSRF-WITNESS
def client_options
verified_url, provider_hostname = verify_blocked_uri
{
host: verified_url.scheme == 'https' ? provider_url : verified_url.to_s,
api_version: 'v1',
hostname: provider_hostname
}
end
Usa verified_url.to_s per https nello stesso modo in cui HTTP già fa. Riscrivi env[:url] in Octokit UrlValidation verso l'IP bloccato.
gitea_controller.rb · legacy_github_import/client.rb · octokit/url_validation.rbGNU Affero GPL v3.0. Vedi LICENSE. Solo lab su loopback. Nessuna garanzia.