Skip to content
KitploitKITPLOIT
ツールエクスプロイトブログ
Log in
提出
ツールエクスプロイトブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

フィードお問い合わせプライバシー© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
jellyfin-suggestions-idor — Jellyfin Server 12.2 - 高 - リモート - IDOR - GET /Items/Suggestions omit-userId が隠しライブラリのパスとIDをダンプ | Kitploit
ツール/GitHubGitHub/abraxas/jellyfin-suggestions-idor
脆弱性分析ウェブアプリケーション悪用情報収集ウェブセキュリティペネトレーションテストAPIセキュリティラボと実践
GitHubabraxas/jellyfin-suggestions-idor

jellyfin-suggestions-idor

Jellyfin Server 12.2 - 高 - リモート - IDOR - GET /Items/Suggestions omit-userId が隠しライブラリのパスとIDをダンプ

リポジトリを見る
11日前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

Abraxas Labs - jellyfin-suggestions-idor

abraxaslabs.tech  ·  github.com/abraxas  ·  @abraxas_null  ·  [email protected]  ·  jellyfin-suggestions-idor

jellyfin-suggestions-idor

Jellyfin Server 12.2 - Jellyfin

分類: IDOR。到達範囲: リモート(認証済みの非管理者なら誰でも)。

GET /Items/Suggestions は [Authorize] のみです。userId が省略されると、SuggestionsController.GetSuggestions は user = null を設定し、RequestHelpers.GetUserId を決して呼び出しません。InternalItemsQuery(null) は SetUser を行いません。LibraryManager.GetItemsResult は AddUserToQuery をスキップします。new DtoOptions() には ItemFields.Path が含まれます。GetBaseItemDtos(..., user: null) は IsVisible をスキップします。

GHSA-9x85 は GetUserId を教えました(省略時はクレームにフォールバックし、外部の GUID は 403 になります)。ItemsController.GetItems は常にこれを使用します。Suggestions は使用していませんでした。レガシーの GET /Users/{userId}/Suggestions は常にルート GUID を持つため、その兄弟エンドポイントは GetUserId を経由します。

制限付きアカウントが、割り当てられていないライブラリのタイトル、ID、概要、プロバイダー ID、ファイルシステム Path をダンプします。それらの GUID は未認証の GET /Videos/{id}/stream で再生できます。

IDCVE 未割り当て
CWECWE-639 / CWE-862
CVSSHigh: 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
製品Jellyfin Server
影響を受けるバージョンv12.2 (ca0f16eb7c195f720a1493ed469a27c4657db0c6)、イメージ jellyfin/jellyfin:12.2
認証認証済み(非管理者なら誰でも、子供 / 非表示ライブラリアカウントを含む)
ライセンスGNU Affero GPL v3.0
ラボ127.0.0.1:18790 のみ

Jellyfin 12.2 leftovers の残存 #1。同じピン、他のパック: jellyfin-svg-gzip(GHSA-cf3c の残存 gzip data:image/png)、jellyfin-playlist-itemids(GHSA-9x85 の残存 ItemIds + 未認証ストリーム)、jellyfin-ffmpeg-colonslash(GHSA-wwwm の残存 .strm URL クォート)。このパックは非表示ライブラリをダンプするものです。

攻撃者ができること

認証済みの非管理者なら誰でも、子供 / 非表示ライブラリ / MaxParentalRating アカウントを含みます。通常のユーザートークンが必要です。管理者権限も API キーも不要です。パスワードは変更しません。管理者権限を生成しません。割り当てられていないライブラリ(ファイルシステム Path を含む)を棚卸しし、それらの GUID を再生します。

権限テーブル(網羅的)。Signal-Server Dropwizard admin と同じ基準: メソッド + パス、その呼び出しが付与するもの、偵察 vs 完全性 vs 可用性、ラボで実証済み vs 推測、閉じたままの隣接サーフェス。

軸メソッド + パス付与されるもの根拠
偵察userId なしの GET /Items/Suggestions(任意で limit / mediaType / type)スコープ外の再帰的アイテム一覧: Name、Id、Overview、プロバイダーID、ファイルシステムパス、呼び出し元が割り当てられていないライブラリの画像メタデータ。これには非表示ユーザーにのみ付与されたフォルダも含まれる。フォルダACL、視聴年齢制限、許可タグは適用されない。ラボ実証済み。 制限付き labuser(EnableAllFolders=false、EnabledFolders=PublicB のみ): GET /Items witness=0、Suggestions witness=1
偵察limit を省略した同じリクエストInternalItemsQuery.Limit が未設定のままとなり、リポジトリは一致する全件(アイテムテーブル全体、ランダム順)を返すソース(SuggestionsController.cs:70-95)
偵察その一覧から得たGUIDを使った未認証の GET /Videos/{itemId}/streamjellyfin UID としてのそのファイルのバイト列ソース(VideosController にはクラスレベルの [Authorize] がない。StreamingHelpers の GetItemById はユーザーなしで動作)。ストリームは姉妹の残存物 jellyfin-playlist-itemids でラボ検証済みであり、本パックではない
完全性Suggestions にはなしパスワード、ポリシー、お気に入り、セッションへの書き込みはなしクローズ
可用性Suggestions にはなし通常のクエリコストを超えるDoSはなしクローズ

このピンではクローズのまま

軸メソッド + パスクローズである理由
偵察GET /Items?userId= および GET /Items?parentId=<hiddenFolder>常に GetUserId + IsVisible(ItemsController.cs:265, 342-350)
偵察GET /Playlists/{id}/Items?userId=GetUserId(PlaylistsController.cs:524)。GHSA-9x85 クラス、修正済み
偵察GET /Items/Suggestions?userId=<other>他人のGUIDでも GetUserId に到達し、非管理者には 403 を返す。ラボ実証済み(impersonate-403=yes)
偵察レガシーな GET /Users/{userId}/SuggestionsルートGUIDが必須のため、確かに GetUserId を呼び出す
完全性POST /Users/Password?userId=AssertCanUpdateUser
完全性POST /Users/{id}/PolicyRequiresElevation
完全性デバイス、セッション、AddAdditionalUser昇格または自身のセッションのみ
偵察GET /Users / GET /Users/{id}UserDto + ポリシー(フォルダGUID、管理者ビット)であり、ライブラリの内容やハッシュではない。世帯の列挙であり、この残存物ではない

私はプラグインDLLによるRCE、リバースシェル、パスワード窃取を主張しているのではない。このダンプは、呼び出し元が割り当てられていないライブラリのカタログ(およびパス)である。GUIDによるストリーミングの後続は、JellyfinのHTML5/DLNAにおける長年の挙動であり、この残存物はそのIDを列挙するものである。

発見の経緯

v12.2 は 12.0 の GHSA の波をクローズした。それでも私はそのタグに留まり、RequestHelpers.GetUserId の隣にあるすべての userId クエリを読んだ。Items は常にこれを使う。Suggestions は分岐する: userId が空 -> user = null となり、GetUserId を決して呼ばない。これが残存物である。

run.sh は2つの映画ライブラリ(witness 名を持つ PrivateA と PublicB)をシードし、ウィザードを完了し、EnableAllFolders=false かつ EnabledFolders=PublicB のみで labuser を作成し、その後 GET /Items と GET /Items/Suggestions を比較する。

すでに記録済みの誤った道: GET /Items をリークとみなすこと(スコープされている); Suggestions?userId=<admin> をリークとみなすこと(403); レガシーな /Users/{userId}/Suggestions ルートを穴とみなすこと(GUIDがあるため GetUserId を呼ぶ); 未認証ストリームを本パックのバグと主張すること(姉妹の残存物); リバースシェル。すべて見せかけである。

クライアントのバグは製品のバグのように見える。http.client をシャドウするヘルパーはパケットを一切送信しない。HTTPResponse に対する .recv() は .read() ではない。これに一度触れたのは、時間を要し、一瞬だけ 12.2 が仕事を終えたように見えたからである。

ラボ

python3 jellyfin-suggestions-idor-Abraxas-Labs.py

または

cd lab
./run.sh

必ず http://127.0.0.1:18790 のみを対象とすること。イメージは jellyfin/jellyfin:12.2。Docker が必要。ランチャーは lab/ に chdir して run.sh を exec する。

IOC user-items http=200 witness=0
IOC user-suggestions http=200 witness=1
IOC impersonate-suggestions http=403
SUCCESS jellyfin-suggestions-idor items-hidden=yes suggestions-has-witness=yes impersonate-403=yes JELLYFIN-SUGGESTIONS-IDOR-WITNESS

制限付きの labuser は /Items 上の PrivateA を見ることができません。同じトークンは userId なしで /Items/Suggestions 上の witness を見ることができます。管理者 GUID を渡しても依然として 403 になります。

修正

ItemsController が既に行っているように、userId が空の場合は RequestHelpers.GetUserId を呼び出します。外部の GUID に対しては 403 を維持します。

var requestUserId = RequestHelpers.GetUserId(User, userId);
user = _userManager.GetUserById(requestUserId);

userId.IsNullOrEmpty() -> user = null 分岐を削除します。パッチ適用済みビルドに対してループバッククライアントを再実行します。制限された labuser の Suggestions は /Items と一致しなければなりません(witness=0)。

それまでは、「/Items をスコープした」を「BaseItemDto を返すすべてのクエリをスコープした」と扱わないでください。空の userId と user = null はインスタンス全体のカタログです。

参考資料

ツールをダウンロード