
Forgejo 16.0.5 - 高 - リモート - ファイル読み取り - restore-repo がダンプ側の file:// を添付ファイルへ追従させる
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · forgejo-restore-file-open
解説記事: https://abraxaslabs.tech/research/forgejo-restore-file-open
クラス: ファイル読み取り 到達範囲: ローカル
Forgejo 16.0.5 (8d24b7d) - Forgejo
信頼できないダンプに対する forgejo restore-repo は、リリースアセットの URL を生の filepath.Join(baseDir, download_url) で書き換え、file:// を付加する。uri.Open は依然としてそのパスを os.Open する。--units が空の場合、リリースが復元される。Gitea 1.27.0 では localFileURL が追加され、エントリのエスケープがスキップされた。そのヘルパーはここには存在しない。
信頼できないダンプのオペレータによる復元は、forgejo uid が開けるホストファイルをリリース添付ファイルへコピーする。
| ID | CVE 未割当 |
| クラス | ファイル読み取り |
| 到達範囲 | ローカル(信頼できないダンプに対するオペレータの forgejo restore-repo) |
| CWE | CWE-22 / CWE-73 |
| CVSS | High: 7.3 CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N |
| 製品 | Forgejo |
| 影響範囲 | v16.0.5(8d24b7d)まで。ローカルオペレータによる復元 |
| 認証 | 信頼できないダンプに対するローカルオペレータの復元 |
| ライセンス | GNU Affero GPL v3.0 |
| ラボ | 127.0.0.1 のみ |
オペレータにダンプディレクトリを渡す(または既に INTERNAL_TOKEN とサーバーが読み取れるパスを保持している)。ユニットには releases / release_assets が含まれる。
| 権限 | メソッド / シンク | 偵察 / 完全性 / 可用性 | ラボ実証または推測 |
|---|---|---|---|
ダンプから .. で抜け出し、forgejo uid が開けるホストファイルを読み取る | release.yml の download_url: ../FORGEJO-FILE-WITNESS.txt。復元時に結合され、uri.Open が os.Open し、バイト列が witness.txt として保存される | 偵察(ホストファイルの機密性) | ラボ実証済み。添付ファイルの本文は FORGEJO-RESTORE-FILE-OPEN-WITNESS。ダンプ内のデコイ INSIDE-DUMP-NOT-HOST は配信されなかった |
| ダンプ内に仕込んだシンボリックリンクでも同じ結果 | 結合後のパスは baseDir 配下に留まるが、os.Open がリンクを辿る | 偵察 | uri.Open の file -> os.Open から推測。ラボではシンボリックリンクではなく .. を使用 |
| PR パッチに対する同じシンク | pull_request.yml の PatchURL も同様に書き換えられ、その後 uri.Open により repo/pulls/<n>.patch へ | 偵察 | restore.go の GetPullRequests から推測。ラボではリリースのみ復元 |
| 復元されたリポジトリの閲覧者がホストファイルをダウンロードする | GET /<owner>/<repo>/releases/download/<tag>/<name> | 偵察 | 復元された公開リポジトリに対し labadmin の Basic 認証でラボ実証済み |
典型的なホストファイル: app.ini、INTERNAL_TOKEN、SECRET_KEY、DB パスワード | forgejo uid は設定ツリーを読み取れる | 偵察 | 推測。ラボの証人ファイルはダンプの隣に置いたものであり、実際のシークレットではない |
ここで塞がれているもの: ライブな POST /repos/migrate の file:// LFI(DownloadFunc が設定され、PR の PatchURL はクローン HTTP ベースに対してプレフィックス検査される)。絶対パスの DownloadURL によるダンプルート結合の脱出(Go の filepath.Join("/dump", "/etc/passwd") は /dump/etc/passwd のまま)。未認証の HTTP 復元(CLI / 内部トークンのみ)。バックアップ dump が app.ini を梱包する件(設計上シークレットを含み、アーカイブモードは 0600)。OS LPE ではない。リモート LFI ではない。
復元に関するアイデアが 2 つあった。まず Gitea の絶対パス結合について頭の中でラボ検証した。Go の filepath.Join は、Python の os.path.join のように絶対パスの第 2 引数を捨てたりはしない。機能する入力は相対パスの .. である。Gitea の localFileURL はこのツリーには存在しない。
誤った方向転換はすでに記録済み: 絶対パス DownloadURL による dump-root 結合エスケープ; ライブマイグレーション file:// LFI; X-Forgejo-Internal-Auth(リストアは forgejo uid としての CLI であり、/api/internal ではない); リストア HTTP をリモート未認証として扱うこと; リバースシェル。茶番。
python3 forgejo-restore-file-open-Abraxas-Labs.py
または:
cd lab
./run.sh
対象は http://127.0.0.1:19340 のみ。イメージ codeberg.org/forgejo/forgejo:16.0.5。ウィットネス FORGEJO-RESTORE-FILE-OPEN-WITNESS。
IOC asset name='witness.txt' ...
IOC download status=200 bytes=34 snippet=b'FORGEJO-RESTORE-FILE-OPEN-WITNESS\n'
SUCCESS forgejo-restore-file-open how=live ..-join file:// os.Open FORGEJO-RESTORE-FILE-OPEN-WITNESS
EvalSymlinks の後、結合されたパスを baseDir 配下に封じ込める(Gitea PR 38215 localFileURL)。.. を拒否する。ダンプ側のシンボリックリンクをたどってダンプ外へ出ないようにする。ダンプルートに限定したヘルパーを除き、uri.Open から file:// を削除する。
restore.go · uri.goGNU Affero GPL v3.0。LICENSE を参照。ループバックラボ専用。無保証。