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
漏洞分析Web应用程序漏洞利用信息收集Web安全渗透测试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(省略时回退到 claims;外部 GUID 返回 403)。ItemsController.GetItems 始终使用它。Suggestions 却没有。旧版 GET /Users/{userId}/Suggestions 始终带有路由 GUID,因此该同级接口确实会经过 GetUserId。

受限账户可以导出其未被分配库的标题、ID、简介、提供方 ID 以及文件系统 Path。这些 GUID 可在未认证的 GET /Videos/{id}/stream 上播放。

ID暂无 CVE
CWECWE-639 / CWE-862
CVSS高危: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 推断、保持关闭的相邻攻击面。

轴方法 + 路径授予内容来源
侦察GET /Items/Suggestions,不带 userId(可选 limit / mediaType / type)无作用域的递归条目列表:Name、Id、Overview、提供程序 ID、文件系统 Path、调用者未被分配的库的图像元数据,包括仅授予隐藏用户的文件夹。文件夹 ACL、家长分级和允许标签均未应用。实验室验证。 受限的 labuser(EnableAllFolders=false,仅 EnabledFolders=PublicB):GET /Items witness=0;Suggestions witness=1
侦察同上,省略 limitInternalItemsQuery.Limit 保持未设置;存储库返回完整匹配集(整个条目表,随机顺序)源码(SuggestionsController.cs:70-95)
侦察未认证 GET /Videos/{itemId}/stream,使用该列表中的 GUID以 jellyfin 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、反向 shell 或密码窃取。该转储是调用者未被分配的库的目录(以及 Path)。按 GUID 流式传输的后续行为是 Jellyfin HTML5/DLNA 的长期行为;此遗留项是枚举这些 ID 的部分。

我是如何发现的

v12.2 关闭了 12.0 GHSA 浪潮。我仍然停留在那个标签上,阅读了每个 userId 查询旁边的 RequestHelpers.GetUserId。Items 始终使用它。Suggestions 分支:空 userId -> user = null,从不调用 GetUserId。这就是遗留项。

run.sh 播种两个电影库(PrivateA 以 witness 命名,PublicB),完成向导,创建 labuser,设置 EnableAllFolders=false 且仅 EnabledFolders=PublicB,然后比较 GET /Items 与 GET /Items/Suggestions。

已记录的弯路:将 GET /Items 视为泄漏(它是有作用域的);将 Suggestions?userId=<admin> 视为泄漏(403);将旧版 /Users/{userId}/Suggestions 路由视为漏洞(它有 GUID,因此会调用 GetUserId);声称未认证流是本包的 bug(同类遗留项);反向 shell。都是表演。

客户端 bug 看起来像产品 bug。一个遮蔽 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。启动器会 chdir 进入 lab/ 并执行 run.sh。

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。同一个令牌在 /Items/Suggestions 上能看到见证内容,且没有 userId。传入管理员 GUID 仍然返回 403。

修复

当 userId 为空时调用 RequestHelpers.GetUserId,就像 ItemsController 已经做的那样。对外部 GUID 保持 403。

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

删除 userId.IsNullOrEmpty() -> user = null 分支。针对已修补的构建重新运行 loopback 客户端:受限 labuser 的 Suggestions 必须与 /Items 一致(witness=0)。

在此之前,不要把“我对 /Items 做了作用域限制”当作“我对所有返回 BaseItemDto 的查询都做了作用域限制”。空的 userId 加上 user = null 意味着实例级目录。

参考资料

下载工具