Prova de conceito e laboratório para um IDOR (CWE-639) no Gitea em GET /api/v1/user/keys/{id}, permitindo que um token autenticado read:user enumere as chaves SSH e deploy keys de outros usuários.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitea-user-keys-idor
Gitea 1.27.3 - Gitea
GET /api/v1/user/keys/{id} fica no grupo do usuário atual (reqToken, rejectPublicOnly). O handler ainda carrega a linha pelo ID numérico sem verificação de proprietário. A mesma tabela contém chaves de usuário, deploy keys e principals. O equivalente do GPG faz certo: GetGPGKeyForUserByID(ctx, ctx.Doer.ID, id). Campos privados são protegidos. O material da chave pública, o título e a impressão digital não são. Os IDs são sequenciais.
Um token read:user percorre 1, 2, 3 e inventaria as chaves SSH e deploy keys de outras pessoas. O registro aberto transforma isso em um bug de conta nova.
| ID | sem CVE ainda |
| CWE | CWE-639 |
| CVSS | Alto: 7.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N |
| Produto | Gitea |
| Afetado | até v1.27.3 (146cc3e) |
| Autenticação | autenticado (token read:user) |
| Licença | GNU Affero GPL v3.0 |
| Lab | apenas 127.0.0.1 |
Registrar-se (registro aberto por padrão), gerar um token read:user, percorrer /api/v1/user/keys/{id}. Ele obtém títulos, impressões digitais e material de chave pública de chaves que não lhe pertencem, incluindo deploy keys. Ele não obtém a chave privada. Ele não obtém um shell. Ele inventaria quem tem acesso a quê.
O GHSA de chaves da 1.27.3 era uma listagem com escopo de nome de usuário mais individualPermsChecker. Não era esta consulta.
Mesma tag, resquício: SSRF de redirecionamento HTTP do git.
A v1.27.3 fechou a onda de GHSAs de 2026. Mesmo assim, fiquei nessa tag e li o handler de chaves do usuário atual ao lado do GPG. O GPG retorna 404 para um id estrangeiro. O SSH não.
seed.sh planta o título da chave da vítima GHSA-GITEA-KEY-IDOR-WITNESS e uma chave do atacante. A própria lista do atacante não contém a testemunha. GET /user/keys/1 como o atacante contém. O GPG com um id estrangeiro retorna 404. Esse é todo o argumento.
Caminhos errados já registrados: tratar a própria lista do atacante como o vazamento (não é - a lista tem escopo, o GET por id não); um reverse shell. Teatro.
Bugs de cliente parecem bugs de produto. Um helper que sombreia http.client nunca envia um pacote. .recv() em um HTTPResponse não é .read(). Menciono isso uma vez porque custou tempo e pareceu, por um minuto, que a 1.27.3 tinha concluído o trabalho.
cd lab
./run.sh
Alvo apenas http://127.0.0.1:18101.
add-key user=victim status=201 title=GHSA-GITEA-KEY-IDOR-WITNESS id=1
add-key user=attacker status=201 id=2
attacker-list status=200 ids=[2] (no witness)
get id=1 status=200 snippet contains GHSA-GITEA-KEY-IDOR-WITNESS
SUCCESS Gitea #1 user/keys IDOR
404 a menos que key.OwnerID == ctx.Doer.ID (ou administrador do site). Rejeitar tipos deploy/principal. Igualar ao GPG.
GetPublicKeyGNU Affero GPL v3.0. Veja LICENSE. Apenas lab em loopback. Sem garantia.