
Forgejo 16.0.5 - 高 - リモート - IDOR - GET /api/v1/user/keys/{id} が他のユーザーの SSH キーおよびデプロイキーを返す
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · forgejo-keys-idor
解説記事: https://abraxaslabs.tech/research/forgejo-keys-idor
分類: IDOR 到達範囲: リモート
Forgejo 16.0.5 (8d24b7d) - Forgejo
GET /api/v1/user/keys/{id} は現在のユーザー用グループ(reqToken、ユーザースコープ)に属している。しかしハンドラは依然として数値IDで public_key を読み込む際に所有者チェックを一切行わない。同じテーブルにはユーザーキー、デプロイキー、そしてプリンシパルが格納されている。このバージョンの GPG は正しく実装されている: GetGPGKeyForUserByID(ctx, ctx.Doer().ID, id)。プライベートな追加フィールドは制限されている。しかし公開鍵の素材、タイトル、フィンガープリントは制限されていない。ID は連番である。
read:user トークンがあれば 1、2、3 と順に走査し、他人の SSH キーやデプロイキーを一覧化できる。
| ID | CVE 未割当 |
| 分類 | IDOR |
| 到達範囲 | リモート(認証済み read:user / セッション) |
| 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 |
| 製品 | Forgejo |
| 影響範囲 | v16.0.5(8d24b7d)まで |
| 認証 | 認証済み(read:user トークンまたはセッション) |
| ライセンス | GNU Affero GPL v3.0 |
| ラボ | 127.0.0.1 のみ |
任意のアカウントを登録または使用し、read:user トークンを発行する(またはセッション Cookie を使用する)。そして /api/v1/user/keys/{id} を順に走査する。これにより、自分が所有していないキーのタイトル、フィンガープリント、公開鍵素材を取得できる。秘密鍵は取得できない。シェルも取得できない。しかし誰が何にアクセスできるかを一覧化できる。
| 権限 | メソッド / シンク | 偵察 / 完全性 / 可用性 | ラボ実証または推測 |
|---|---|---|---|
| 他ユーザーの SSH 公開鍵のタイトル、フィンガープリント、鍵素材を読み取る | 他所有者としての GET /api/v1/user/keys/{id} | 偵察(公開鍵インベントリの機密性) | ラボ実証済み。id 1 に目印タイトル FORGEJO-KEYS-IDOR-WITNESS を配置。攻撃者の一覧には id 2 のみが表示された |
| 同様の方法でデプロイキーを一覧化する | 同じハンドラ、public_key 内の KeyTypeDeploy 行(OwnerID 0) | 偵察 | テーブルから推測。ラボではユーザー SSH キーを設置し、デプロイキーは設置していない |
| SSH 証明書のプリンシパルを読み取る | 同じハンドラ、KeyTypePrincipal 行。ListPublicKeys はこの型を除外している | 偵察 | テーブルから推測 |
| 制限付きまたはプライベートユーザーに属するキーを閲覧する | この参照に可視性チェックがない | 偵察 | ハンドラから推測 |
追加の JSON(user、key_type、read_only) | appendPrivateInformation は管理者または所有者の場合のみ | 偵察 | ラボ実証済み(クローズ)。非所有者のレスポンスには user フィールドがなかった |
このルートでクローズ済みのもの: 秘密鍵素材、シェル、GET /users/{username}/keys(ユーザー名スコープの公開一覧)、GPG GET /api/v1/user/gpg_keys/{id}(他者の id では 404)、DeletePublicKey(所有者/管理者は 403)。クロスリポジトリの GET /repos/{owner}/{repo}/keys/{id} は RepoID が一致しない場合 404 を返す。
v16.0.5 のセキュリティパッチ(#14370)は git-apply RCE と OpenID CSRF に関するものだった。この参照はクローズされなかった。gitea-user-keys-idor と同種の問題であり、ここでも独立して依然として未修正である。
2026-09 の波の後、Forgejo v16.0.5 を固定した。Gitea 1.27.3 にもこの参照が残っていた。Forgejo は分岐しているため、Gitea のパックをコピーするのではなく、現在のユーザー用キーハンドラを GPG と並べて読み直した。GPG は他者の id で 404 を返す。SSH は返さない。
誤った方向転換はすでに記録済みである。攻撃者自身のリストを漏洩とみなすこと(そうではない — リストはスコープ制限されているが、GET-by-id はそうではない)、インストーラの DISABLE_REGISTRATION=true をクローズとみなすこと(既存のアカウントが1つあれば十分である)、リバースシェル。すべて見せかけである。
python3 forgejo-keys-idor-Abraxas-Labs.py
または:
cd lab
./run.sh
対象は http://127.0.0.1:19310 のみ。イメージ codeberg.org/forgejo/forgejo:16.0.5。2つのアカウント。ウィットネス 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
key.OwnerID == ctx.Doer().ID(またはサイト管理者)でない限り404を返す。このルートでは KeyTypeDeploy / KeyTypePrincipal を拒否する。GetGPGKeyForUserByID に合わせる。
GetPublicKeyGNU Affero GPL v3.0。LICENSE を参照。ループバックラボ専用。保証なし。