
Forgejo 16.0.5 – Hoch – Remote – IDOR – GET /api/v1/user/keys/{id} gibt die SSH- und Deploy-Schlüssel anderer Benutzer zurück
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · forgejo-keys-idor
Klasse: IDOR Reichweite: Remote
Forgejo 16.0.5 (8d24b7d) - Forgejo
GET /api/v1/user/keys/{id} liegt in der Gruppe für den aktuellen Benutzer (reqToken, Benutzer-Scope). Der Handler lädt public_key dennoch anhand der numerischen ID, ohne Besitzerprüfung. Dieselbe Tabelle enthält Benutzerschlüssel, Deploy Keys und Principals. GPG macht es an diesem Pin richtig: GetGPGKeyForUserByID(ctx, ctx.Doer().ID, id). Private Zusatzfelder sind geschützt. Das öffentliche Schlüsselmaterial, der Titel und der Fingerprint sind es nicht. IDs sind sequenziell.
Ein read:user-Token läuft 1, 2, 3 durch und inventarisiert die SSH- und Deploy-Keys anderer Personen.
| ID | noch keine CVE |
| Klasse | IDOR |
| Reichweite | Remote (authentifiziert read:user / Session) |
| 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 | Forgejo |
| Betroffen | bis v16.0.5 (8d24b7d) |
| Auth | authentifiziert (read:user-Token oder Session) |
| Lizenz | GNU Affero GPL v3.0 |
| Lab | nur 127.0.0.1 |
Ein beliebiges Konto registrieren oder verwenden, ein read:user-Token erzeugen (oder ein Session-Cookie nutzen), /api/v1/user/keys/{id} durchlaufen. Man erhält Titel, Fingerprints und öffentliches Schlüsselmaterial für Schlüssel, die einem nicht gehören. Man erhält nicht den privaten Schlüssel. Man erhält keine Shell. Man inventarisiert, wer Zugriff auf was hat.
| Privileg | Methode / Sink | Recon / Integrität / Verfügbarkeit | Lab-bestätigt oder abgeleitet |
|---|---|---|---|
| Titel, Fingerprint und Schlüsselmaterial des SSH-Public-Keys eines anderen Benutzers lesen | GET /api/v1/user/keys/{id} als fremder Besitzer | Recon (Vertraulichkeit des Public-Key-Inventars) | Lab-bestätigt. Witness-Titel FORGEJO-KEYS-IDOR-WITNESS auf id 1; Angreifer-Liste enthält nur id 2 |
| Deploy Keys auf dieselbe Weise inventarisieren | Derselbe Handler, KeyTypeDeploy-Zeilen in public_key (OwnerID 0) | Recon | Aus der Tabelle abgeleitet. Lab hat einen Benutzer-SSH-Key platziert, keinen Deploy Key |
| SSH-Zertifikats-Principals lesen | Derselbe Handler, KeyTypePrincipal-Zeilen. ListPublicKeys schließt diesen Typ aus | Recon | Aus der Tabelle abgeleitet |
| Schlüssel sehen, die eingeschränkten oder privaten Benutzern gehören | Keine Sichtbarkeitsprüfung bei diesem Lookup | Recon | Aus dem Handler abgeleitet |
Zusätzliches JSON (user, key_type, read_only) | appendPrivateInformation nur bei Admin oder Besitzer | Recon | Lab-bestätigt geschlossen: Nicht-Besitzer-Antwort hatte kein user-Feld |
Geschlossen auf dieser Route: privates Schlüsselmaterial, eine Shell, GET /users/{username}/keys (benutzernamenbezogene öffentliche Liste), GPG GET /api/v1/user/gpg_keys/{id} (404 bei fremder id), DeletePublicKey (Besitzer/Admin 403). Cross-Repo GET /repos/{owner}/{repo}/keys/{id} liefert 404, wenn RepoID nicht übereinstimmt.
Die Sicherheitspatches von v16.0.5 (#14370) waren git-apply RCE und OpenID CSRF. Sie haben diesen Lookup nicht geschlossen. Gleiche Klasse wie gitea-user-keys-idor, hier unabhängig weiterhin offen.
Ich habe Forgejo v16.0.5 nach der Welle von 2026-09 gepinnt. Gitea 1.27.3 hatte diesen Lookup noch. Forgejo hat sich abgespalten, also habe ich den Key-Handler für den aktuellen Benutzer neben GPG neu gelesen, statt das Gitea-Paket zu kopieren. GPG liefert 404 bei einer fremden id. SSH nicht.
Bereits dokumentierte Irrwege: die eigene Liste des Angreifers als Leak zu behandeln (ist sie nicht - die Liste ist gescoped, GET-by-id nicht); DISABLE_REGISTRATION=true im Installer als Schließung zu behandeln (jedes bestehende Konto genügt); eine Reverse Shell. Theater.
python3 forgejo-keys-idor-Abraxas-Labs.py
oder:
cd lab
./run.sh
Ziel nur http://127.0.0.1:19310. Image codeberg.org/forgejo/forgejo:16.0.5. Zwei Konten. Witness FORGEJO-KEYS-IDOR-WITNESS.
IOC planted victim-id=1 attacker-id=2
IOC attacker-list status=200 ... title=attacker-own-key
IOC get id=1 status=200 ... FORGEJO-KEYS-IDOR-WITNESS
IOC gpg-neg id=1 status=404
SUCCESS forgejo-keys-idor victim-id=1 get-http=200 list-has-id=no gpg-http=404 FORGEJO-KEYS-IDOR-WITNESS
404, außer key.OwnerID == ctx.Doer().ID (oder Site-Admin). KeyTypeDeploy / KeyTypePrincipal auf dieser Route ablehnen. GetGPGKeyForUserByID entsprechen.
GetPublicKeyGNU Affero GPL v3.0. Siehe LICENSE. Nur Loopback-Lab. Keine Gewährleistung.