
Forgejo 16.0.5 - Alta - Remoto - SSRF - A descoberta do OpenID busca URLs controladas pelo atacante
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · forgejo-openid-discovery-ssrf
Classe: SSRF Alcance: Remoto
Forgejo 16.0.5 (8d24b7d) - Forgejo
Quando [openid] ENABLE_OPENID_SIGNIN está ativado, o POST /user/login/openid não autenticado entrega a URL do atacante ao yohcop openid-go v1.0.1. Os helpers em nível de pacote dessa biblioteca usam http.DefaultClient. Não há hostmatcher nesse caminho. Webhooks e migrações filtram o HTTP de saída. O OpenID não.
Alguém que consiga alcançar o formulário de login pode disparar um GET Yadis contra loopback, RFC1918, CGNAT e IMDS a partir do host Forgejo.
| ID | ainda sem CVE |
| Classe | SSRF |
| Alcance | Remoto (não autenticado, quando o login OpenID está ativado) |
| CWE | CWE-918 |
| CVSS | Alto: 8.6 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N |
| Produto | Forgejo |
| Afetado | até v16.0.5 (8d24b7d) com ENABLE_OPENID_SIGNIN; resquício do Gitea CVE-2026-58314 |
| Autenticação | não autenticado |
| Licença | GNU Affero GPL v3.0 |
| Lab | somente 127.0.0.1 |
Sem conta. Envie um POST com uma URL OpenID. O host Forgejo a busca.
| Privilégio | Método / sink | Recon / integridade / disponibilidade | Comprovado em lab ou inferido |
|---|---|---|---|
| Disparar um GET Yadis contra loopback | POST /user/login/openid não autenticado com openid=http://127.0.0.1:18080/ | Recon (HTTP de saída a partir do host Forgejo) | Comprovado em lab. O oráculo viu GET / com Accept: application/xrds+xml de 127.0.0.1 |
| Sondar RFC1918, CGNAT, link-local, IMDS | Mesmo http.DefaultClient Discover, sem filtro de IP | Recon | Inferido a partir do cliente. O lab usou apenas loopback |
| Repetir a partir de um add-OpenID autenticado | POST /user/settings/security/openid também chama RedirectURL e ignora allowedOpenIDURI | Recon | Inferido a partir do handler. O lab usou o POST de login não autenticado |
| Ler corpos de resposta no erro de login | Após CVE-2021-45325, os erros não despejam mais a busca | Recon | Comprovado em lab que está fechado. Este resquício é a requisição, não um vazamento de HTML |
Fechado neste pin: CSRF de associação OpenID (nonce de sessão openIDState), hostmatcher de webhook/migrate (ainda usado lá), git followRedirects=false, migrate LookupIP CVE-2026-82556. O lab não completa um login OpenID e não retorna um shell.
A caixa de seleção do instalador web vem ativada por padrão (EnableOpenIDSignIn = MustBool(!InstallLock) enquanto desbloqueado, depois gravado em app.ini). Após o lock, uma chave omitida é false. O template app.ini do Docker não tem seção [openid], então uma imagem pré-bloqueada sem o assistente fica desativada a menos que o operador defina a chave. WHITELISTED_URIS vazio permite qualquer URI.
A v16.0.5 fechou o CSRF de associação OpenID. Mesmo assim, eu continuei nessa superfície. O Gitea PR 38406 / CVE-2026-58314 colocou um cliente filtrado por hostmatcher em uma instância dedicada do openid-go. Este pin ainda chama os helpers em nível de pacote.
Caminhos errados já registrados: reenviar o CSRF de associação (esse nonce está na árvore); tratar uma imagem Docker pré-bloqueada como o padrão (o assistente ativa a caixa de seleção); esperar o antigo vazamento de string de erro do OpenID; um reverse shell. Teatro.
python3 forgejo-openid-discovery-ssrf-Abraxas-Labs.py
ou:
cd lab
./run.sh
Alvo somente http://127.0.0.1:19330. Imagem codeberg.org/forgejo/forgejo:16.0.5. O oráculo compartilha o netns do Forgejo e escuta em 127.0.0.1:18080.
IOC post-openid status=200
IOC xrds-hits count=1
SUCCESS forgejo-openid-discovery-ssrf how=live post=/user/login/openid openid=http://127.0.0.1:18080/ path=/ accept=application/xrds+xml peer=127.0.0.1 post-status=200 FORGEJO-OPENID-DISCOVERY-WITNESS
O mesmo que o Gitea PR 38406: um http.Client filtrado por hostmatcher em uma instância dedicada do openid-go. Mantenha WHITELISTED_URIS como segundo portão. Bloqueie loopback, RFC1918, CGNAT, link-local e metadata.
modules/auth/openid/openid.goGNU Affero GPL v3.0. Veja LICENSE. Somente lab em loopback. Sem garantia.