与修复原理)
OpenProject 16.6.6 安全更新深度解析仓库 Diff 参数注入漏洞CVE-2026-24685与修复原理【免费下载链接】openprojectOpenProject is the leading open source project management software for product, project and portfolio management. A powerful Jira alternative with agile planning, issue tracking, roadmaps, Gantt charts, time tracking, collaboration features, and more. Available on premises or in the cloud. ⭐ Star us on GitHub项目地址: https://gitcode.com/GitHub_Trending/op/openprojectOpenProject 16.6.62026-01-27 发布是一次以安全修复为核心的补丁版本重点修复了仓库 Diff 下载端点/projects/:project_id/repository/diff.diff中存在的**参数注入Argument Injection**漏洞 CVE-2026-24685。该漏洞允许具备:browse_repository权限的用户通过构造rev参数实现任意文件写入并在具备仓库写权限时进一步升级为远程代码执行RCE。本文基于该版本发布说明结合当前仓库源码lib/open_project/scm/adapters/git.rb、app/controllers/repositories_controller.rb、config/routes.rb等逐层拆解漏洞成因、攻击链、修复机制与版本升级要点并介绍同版本附带的一项 git diff 修订号解析 bugfix。版本信息与升级背景OpenProject 16.6.6 的发布说明位于 docs/release-notes/16/16-6-6/README.md官方社区版本页为community.openproject.org/versions/2261。该版本包含1 个安全漏洞修复CVE-2026-24685参数注入 → 任意文件写入 → 潜在 RCE1 个常规 Bugfix修复 git diff 输出中的修订号revision解析问题工作包 #71019发布说明明确强烈建议所有部署实例立即升级到最新版本。对于安全敏感的部署方这是一次必修级升级漏洞影响面覆盖所有启用了仓库模块、且存在具备:browse_repository权限用户的实例攻击无需高权限利用成本低。CVE-2026-24685仓库 Diff 端点的参数注入漏洞漏洞端点与触发条件漏洞位于 OpenProject 的仓库 diff 下载端点/projects/:project_id/repository/diff.diff当渲染单个修订single revision时OpenProject 底层通过git show命令生成 diff 内容。该端点在当前仓库的路由定义中位于 config/routes.rbget (/revisions/:rev)/diff.:format, action: :diff get (/revisions/:rev)/diff(/*repo_path), action: :diff, format: html, constraints: { rev: /[\w.-]/, repo_path: /.*/ }值得注意的是第一行diff.:format形态即diff.diff、diff.html等带格式后缀的请求没有对rev参数施加任何字符约束而第二行 HTML 形态才带有rev: /[\w.-]/的约束。这意味着通过diff.diff路径rev参数可以携带任意字符——这是漏洞可利用性的路由基础。对应的路由测试同样覆盖了该端点形态见 spec/routing/repositories_routing_spec.rb。根因用户输入被直接当作 git 命令行选项当请求format为diff时控制器进入 diff 下载分支app/controllers/repositories_controller.rbdef diff if params[:format] diff diff repository.diff(path, rev, rev_to) unless diff show_error_not_found return end filename changeset_r#{rev} filename _r#{rev_to} if rev_to send_data diff.join, filename: #{filename}.diff, type: text/x-patch, disposition: attachment # ...rev来自 URL 参数最终被透传给 Git 适配器的diff方法lib/open_project/scm/adapters/git.rbdef diff(path, identifier_from, identifier_to nil) args build_diff_range(identifier_from, identifier_to) args -- scm_encode(path_encoding, UTF-8, path) unless path.empty? capture_git(args).lines rescue Exceptions::CommandFailed nil end单修订场景下build_diff_range生成的核心命令为lib/open_project/scm/adapters/git.rb[show, --no-abbrev-commit, --no-color, --end-of-options, from_sha]from_sha直接由用户提供的rev派生。关键问题在于git 会把它当作命令行选项来解释而非修订号。由于 OpenProject 通过数组参数而非 shell 字符串拼接执行命令见 lib/open_project/scm/adapters/local_client.rb 的Open3.capture3(client_command, *args, opts)攻击者虽然无法做 shell 注入却可以注入 git 自身的选项——这就是参数注入argument injection的含义。发布说明给出的攻击示例为rev--output/tmp/poc.txt当git show收到--output/tmp/poc.txt时git 会把 diff 输出重定向到攻击者指定的路径从而实现在 OpenProject 进程用户具备写权限的任意路径上创建/覆盖文件。攻击链与影响范围从发布说明与源码可以梳理出完整攻击链前置条件攻击者在目标项目上具备:browse_repository权限仓库浏览权限通常是项目成员的基础权限之一构造请求向diff.diff端点发送精心构造的rev值例如rev--output/tmp/poc.txt同理可注入--output、-o等 git show 支持的输出重定向选项参数注入OpenProject 执行 SCM 命令时git 将攻击者控制的rev当作选项解析把git show的输出commit 元数据与补丁内容写入攻击者选定的路径落地影响任意写入文件的内容为git show的输出提交元数据 patch本身不可控但覆盖应用文件或配置文件依然会造成数据丢失与拒绝服务DoS直接破坏系统的完整性与可用性当攻击者对仓库具备写权限时可以构造特定 commit使git show的输出内容满足攻击者意图例如覆盖可被加载/执行的脚本或配置文件从而升级为以 OpenProject 应用进程权限执行的远程代码执行RCE。漏洞披露信息漏洞由安全研究员sam91281通过YesWeHack.com 的 OpenProject Bug Bounty 计划负责任地披露该计划由欧盟委员会European Commission赞助官方安全公告编号为GitHub Advisory GHSA-74p5-9pr3-r6pw。源码视角修复机制如何封堵参数注入虽然发布说明本身没有给出补丁 diff但从当前仓库源码可以清晰看到针对此类注入的纵深防御措施这些机制共同构成了对 CVE-2026-24685 的修复方案。第一道防线resolve_commit强制解析为合法 SHAGit 适配器在构造任何基于用户输入的 git 命令前先通过resolve_commit将用户提供的任意 commit-ish分支名、标签、HEAD~1等解析为经过验证的 commit SHAlib/open_project/scm/adapters/git.rb# Resolve any user-provided commit-ish (branch, tag, HEAD~1, etc.) to a commit SHA, # so we can be sure to work with a valid commit identifier. def resolve_commit(rev) capture_git([rev-parse, --verify, --quiet, --end-of-options, #{rev}^{commit}]).strip end关键点有三rev-parse只做解析git rev-parse不接受--output这类输出重定向选项攻击者注入的--output...在这里会被当作非法输入--verify --quiet组合下解析失败即静默返回非零退出码capture_git会抛出CommandFailed^{commit}后缀强制要求解析结果必须是一个 commit 对象进一步收紧了输入语义--end-of-options确保#{rev}^{commit}即使以-开头也不会被rev-parse解释为选项。随后build_diff_range使用解析得到的 SHA 构造命令def build_diff_range(identifier_from, identifier_to nil) from_sha resolve_commit(identifier_from) if identifier_to to_sha resolve_commit(identifier_to) [diff, --no-abbrev-commit, --no-color, --end-of-options, to_sha, from_sha] else [show, --no-abbrev-commit, --no-color, --end-of-options, from_sha] end end第二道防线--end-of-options全局收紧--end-of-options是 git 提供的哨兵标记它告诉 git该标记之后的所有参数一律视为位置参数修订号/路径不得再当作选项解析。即使未来某处遗漏了resolve_commit校验只要--end-of-options位于用户输入之前注入的--output...也会被 git 当作修订号字符串处理从而失效。当前仓库的 Git 适配器在所有涉及用户输入的 git 子命令中均使用了该标记包括cat文件内容读取%w|show --no-color --end-of-options|git.rbdiff/单修订展示show --no-abbrev-commit --no-color --end-of-optionsgit.rb双修订 diffdiff --no-abbrev-commit --no-color --end-of-optionsgit.rbrev-parse校验rev-parse --verify --quiet --end-of-optionsgit.rb分支创建/跟踪、update-ref、ls-tree、log等其余 SCM 操作同样遵循该模式git.rb 等。第三道防线无 shell 拼接的进程执行底层执行层lib/open_project/scm/adapters/local_client.rb使用Open3.capture3(client_command, *args, opts)参数以数组形式原样传给进程不经过 shell 解释因此不存在经典的;、|、$()等 shell 注入面。该 CVE 的本质是 git 客户端自身的选项解析面而非 shell 注入这也解释了为何需要--end-of-options与resolve_commit组合拳来根治。修复机制的防御纵深小结防线位置作用resolve_commitrev-parse --verifylib/open_project/scm/adapters/git.rb#L602-L606把用户输入收敛为合法 commit SHA注入的选项无法通过校验--end-of-optionsgit.rb 等多处强制后续参数为位置参数阻断选项注入语义数组参数 Open3.capture3local_client.rb消除 shell 注入面命令参数不被重新解释受影响权限与安全缓解建议受影响权限:browse_repository仓库浏览权限。在 OpenProject 中该权限通常是项目成员默认可获得的基础仓库权限这意味着任何能访问项目仓库页面的普通成员都可能成为攻击者并非只有管理员或仓库维护者可利用性任意文件写入的内容是git show输出直接内容可控性有限但覆盖关键文件即可造成数据丢失与拒绝服务叠加仓库写权限后可构造特定 commit 将可控内容写入目标文件升级为 RCE且 RCE 的执行权限为 OpenProject 应用进程用户缓解措施立即升级到 16.6.6 或更新版本这是发布说明明确要求的第一优先级动作升级前若无法立即停机可临时收紧项目仓库权限移除不可信成员的:browse_repository权限加固运行 OpenProject 进程的操作系统用户权限遵循最小权限原则限制应用进程用户对配置文件、应用目录的写权限降低任意文件写与 RCE 的落地危害关注官方安全公告GitHub Advisory GHSA-74p5-9pr3-r6pw以获取完整的受影响版本范围说明。随版本发布的 Bugfixgit diff 输出修订号解析除安全修复外16.6.6 还修复了一个功能性问题Fix revision parsing in git diff output工作包 #71019即修复了 git diff 输出中修订号revision的解析错误。从当前仓库代码看diff 输出解析涉及两条链路diff 表格渲染链路前端/视图层对git show/git diff输出的解析主要发生在 lib/redmine/diff_table.rb。add_line通过正则^ (\|-)(\d)(,\d)? (\|-)(\d)(,\d)? 解析 hunk 头将 -a,b c,d 中的左右起始行号提取为line_num_l/line_num_r据此为每一行 diff 内容计算正确的行号。若git show输出的修订号/行号信息格式存在边界情况如,count部分缺失、行号格式变化解析就会出现偏差导致 diff 视图中的行号与实际内容错位修订对象构建链路Git 适配器在git log --raw输出解析中构建Git::Revision对象lib/open_project/scm/adapters/git.rbidentifier与scmid取自解析出的 commit 哈希作者、日期、描述分别从author、date、描述区段提取路径变更列表则由--raw输出逐条解析。修订号解析修复即属于这一链路的健壮性改进。该 bugfix 与 CVE 修复并不直接相关但对依赖 diff 视图行号定位的日常使用体验有实际影响。如果你在 16.6.6 之前的版本上遇到过 diff 行号错位或修订号显示异常的问题升级后即可获得修复。升级到 16.6.6发布说明对本次升级的要求非常明确安全修复类发布强烈建议所有实例立即升级。OpenProject 提供多种部署形态升级路径取决于你的部署方式Docker / Docker Compose 部署更新镜像至16.6.6对应 tag 并重建容器仓库根目录提供了 docker-compose.yml 及 docker-compose.override.example.yml 可供参考配置包管理 / 源码部署按官方安装运维文档执行常规升级流程相关文档位于 docs/installation-and-operations云端托管由托管方自动完成无需手动干预。升级完成后建议验证仓库模块的 diff 下载功能/projects/:project_id/repository/diff.diff正常工作并确认 diff 视图的行号显示正确以覆盖本次发布的两项变更。总结OpenProject 16.6.6 是一个典型的安全补丁版本其核心价值在于修复 CVE-2026-24685通过/projects/:project_id/repository/diff.diff端点向git show注入选项实现任意文件写入乃至以应用进程权限执行的 RCE。从当前源码可以清晰看到修复采用的三层纵深防御——resolve_commit把用户输入收敛为合法 commit SHA、--end-of-options阻断 git 选项注入语义、数组参数执行消除 shell 注入面这一组合对同类 SCM 参数注入问题具有普遍的参考价值。配合 #71019 的 diff 修订号解析修复建议所有 16.6.6 之前版本的用户尽快完成升级。【免费下载链接】openprojectOpenProject is the leading open source project management software for product, project and portfolio management. A powerful Jira alternative with agile planning, issue tracking, roadmaps, Gantt charts, time tracking, collaboration features, and more. Available on premises or in the cloud. ⭐ Star us on GitHub项目地址: https://gitcode.com/GitHub_Trending/op/openproject创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考