
Proof-of-Concept und Lab für eine Gitea-IDOR (CWE-639) in GET /api/v1/user/keys/{id}, die es einem authentifizierten read:user-Token ermöglicht, die SSH- und Deploy-Keys anderer Benutzer aufzuzählen.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitea-user-keys-idor
Gitea 1.27.3 - Gitea
GET /api/v1/user/keys/{id} liegt in der Gruppe für den aktuellen Benutzer (reqToken, rejectPublicOnly). Der Handler lädt die Zeile dennoch anhand der numerischen ID, ohne Prüfung des Eigentümers. Dieselbe Tabelle enthält Benutzerschlüssel, Deploy Keys und Principals. Das GPG-Pendant macht es richtig: GetGPGKeyForUserByID(ctx, ctx.Doer.ID, id). Private Felder sind geschützt. Das öffentliche Schlüsselmaterial, der Titel und der Fingerabdruck nicht. IDs sind fortlaufend.
Ein read:user-Token läuft 1, 2, 3 durch und inventarisiert die SSH- und Deploy-Keys anderer Personen. Die offene Registrierung macht daraus einen Bug für neue Konten.
| ID | noch keine CVE |
| CWE | CWE-639 |
| CVSS | High: 7.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N |
| Produkt | Gitea |
| Betroffen | bis v1.27.3 (146cc3e) |
| Auth | authentifiziert (read:user-Token) |
| Lizenz | GNU Affero GPL v3.0 |
| Lab | nur 127.0.0.1 |
Registrieren (standardmäßig offene Registrierung), ein read:user-Token erzeugen, /api/v1/user/keys/{id} durchlaufen. Er erhält Titel, Fingerabdrücke und öffentliches Schlüsselmaterial für Schlüssel, die ihm nicht gehören, einschließlich Deploy Keys. Er erhält nicht den privaten Schlüssel. Er erhält keine Shell. Er inventarisiert, wer Zugriff auf was hat.
Die GHSA zu Keys in 1.27.3 betraf eine benutzernamenbezogene Liste plus individualPermsChecker. Es war nicht dieser Lookup.
Übrig geblieben im selben Tag: git HTTP redirect SSRF.
v1.27.3 schloss die GHSA-Welle von 2026. Ich blieb trotzdem auf diesem Tag und las den Key-Handler für den aktuellen Benutzer neben dem von GPG. GPG liefert bei einer fremden ID einen 404. SSH nicht.
seed.sh legt den Opfer-Key-Titel GHSA-GITEA-KEY-IDOR-WITNESS und einen Angreifer-Key an. Die eigene Liste des Angreifers enthält den Witness nicht. GET /user/keys/1 als der Angreifer schon. GPG mit einer fremden ID liefert einen 404. Das ist das ganze Argument.
Bereits festgehaltene Irrwege: die eigene Liste des Angreifers als Leak zu behandeln (ist sie nicht - die Liste ist eingegrenzt, GET-by-id nicht); eine Reverse Shell. Theater.
Client-Bugs sehen wie Produkt-Bugs aus. Ein Helper, der http.client überschattet, sendet nie ein Paket. .recv() auf einer HTTPResponse ist nicht .read(). Ich erwähne das einmal, weil es Zeit gekostet hat und eine Minute lang so aussah, als hätte 1.27.3 die Sache erledigt.
cd lab
./run.sh
Ziel nur 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, es sei denn key.OwnerID == ctx.Doer.ID (oder Site-Admin). Deploy-/Principal-Typen ablehnen. GPG entsprechen.
GetPublicKeyGNU Affero GPL v3.0. Siehe LICENSE. Nur Loopback-Lab. Keine Gewährleistung.