事件调查
Clop 的勒索网站,成了勒索目标
勒索组织 Clop 的网站被另一团伙改掉并遭索款,事后追出的 Grav 漏洞在旧分支里多留了几个月,仍在使用旧版的站点需要尽快更新,并检查此前有没有遭到入侵。

文章导航
Clop 是一个勒索组织,靠公开受害企业的资料施压。9 月 18 日,它自己的泄露网站先多出一个外人上传的文件,随后首页也被换掉了。BleepingComputer 的记者当时访问网站,确认了文件可下载和页面遭篡改。动手的另一团伙 ShinyHunters 接着向 Clop 索要八位数赎金,威胁公开据称拿到的资料。惯常发布勒索通知的一方,这次收到了别人的通知。
Clop 的页面由 Grav 搭建。这套 PHP 内容管理系统让站长组织文章、页面和表单,许多内容直接保存在文件中。后续采访把入侵入口指向表单临时文件处理,Grav 维护者随后确认:对应漏洞四月已经修过,旧的 1.7 分支却直到事件曝光后的 9 月 24 日才得到这项修复。
攻击者声称 Clop 用的是 1.7.43。它确实落后于当时最新的 1.7.53.3,但即使及时更新到后者,相关代码仍然有问题。其他留在 1.7 的站点也经历了这段空窗:新分支已经修好,自己能安装的旧分支更新里却没有补丁。
1 先找到站点真正运行的那份代码
本次入口涉及 Grav 核心的 FormFlash:它为表单保存临时状态和上传文件。旧代码把表单编号拼进目录名;编号若含有路径含义,文件操作就可能离开预定目录。核心 1.7.43 的 构造函数接收编号,getTmpDir()负责拼接。修复后的 1.7.53.4 在接收时限制编号字符,后面的上传路径会拒绝空编号。只更新 Form 插件,核心里的旧实现仍可能留下。
截至 9 月 30 日,Grav 官网推荐的稳定版是 2.2.3。能够完成兼容性验证的站点应以它为部署目标;暂时还要留在 1.7 的站点,先安装 1.7.53.4 的回补,再安排迁移。2.0.0-beta.2 是四月的首次修复点,今天应使用当前稳定版本。1.7.53.4 解决本篇讨论的缺口,其他依赖和历史安全更新仍需逐项检查。
来不及更新时,先限制外部访问受影响表单及其上传、状态保存入口,并从服务器一侧确认限制生效。仅隐藏网页上的上传按钮,访问者仍可直接提交请求。暂停相关表单可能影响咨询或附件收集,应提供可用的替代渠道。升级失败时继续保留限制,用包含本分支修复、且已验证兼容性的程序包恢复;1.7.53.4 这个修复下限并不保证跨大版本回退时的数据和配置兼容。
2 一个表单编号,怎样决定文件写到哪里
网站收附件时,文件通常先进入临时目录,等表单处理完成后再转到最终位置。Grav 需要知道这份临时文件属于哪个会话、哪张表单,于是把两种编号放进路径。默认布局可以读成 tmp/forms/会话编号/表单编号。这样组织文件很方便,前提是这两个编号始终只是目录中的一个名字。
问题从浏览器带回的表单编号开始。报道中提到的 Form 7.3.0,在 form.php 第 1191–1215 行读取 __unique_form_id__,找到对应表单后调用 setUniqueId()。服务器最初生成过一个正常编号,也不能保证访问者原封不动地把它交回来。隐藏字段只影响网页怎样显示,字段值仍由提交请求的一方提供。
接下来,核心的 FormTrait::getFlash()把这个值作为 unique_id 交给 FormFlash。这里的 flash 指跨请求保留的短期表单状态。构造函数接下编号后,getTmpDir()几乎直接给出了文件系统会使用的路径:
// Grav 1.7.43 · FormFlash::getTmpDir()
return $this->folder && $this->uniqueId
? "{$this->folder}/{$this->uniqueId}"
: '';
比如,正常编号 form_42 会落在会话目录里的同名子目录。目录分隔符和表示上级目录的片段却会改变路径走向。PHP 把拼好的字符串交给文件系统时,文件系统会按这些含义查找位置。错误发生在这里:原本用于区分表单的值,获得了选择存储位置的能力。

还要看谁真正使用了这个路径。Form 插件的 uploadFiles()会调用 flash 对象的上传方法。1.7.43 使用的兼容类 Grav\Common\Form\FormFlash::uploadFile()取得临时目录、创建目录,再调用 move_uploaded_file() 把文件搬进去。到这一步,路径字符串已经变成了磁盘写入位置。
这也解释了为什么检查上传文件的后缀、大小,或者给文件换一个随机名字,仍然各有各的局限。它们可以约束文件内容、数量或最后一段文件名;这里出问题的是父目录。插件在调用上传方法前确实有类型和大小检查,具体站点还受表单配置、功能可达性和 PHP 进程写权限限制。源码足以说明目录逃逸如何形成;Clop 机器上完整的请求、落地文件和后续执行过程,公开材料没有提供。
3 四月的修复,九月才到旧分支
四月的 修复提交 d904efc为路径编号加上严格的字符限制,进入 2.0.0-beta.2。9 月 24 日,a39fe54才把相应修复带回 1.7,随 1.7.53.4 发布。这种“回补”让暂时无法升级大版本的用户也能修掉同一个错误。维护者在后续采访中确认了此前没有回补,公开记录没有解释为何隔了这几个月。
1.7.53.4 的 sanitizeId()用下面的正则检查编号,匹配失败就把整个值置空。它没有把坏字符删掉后继续使用剩余部分,因而不会把两种不同输入悄悄修剪成同一个目录名。
// Grav 1.7.53.4 · sanitizeId() 的接收条件
preg_match('/^[A-Za-z0-9,_-]{1,64}$/', $id) ? $id : '';
空编号怎样阻止前面那次写入?getTmpDir()在编号为空时返回空路径;兼容类的 uploadFile()随即返回失败,目录还没有创建,文件也还没有搬进去。表单状态保存另有空值检查。其他插件或自定义表单若使用不同的文件操作,仍要检查各自的调用路径。
旧分支还保留了一处有意的差别。2.x 的实现限制 id、session_id、unique_id 三个字段;1.7 回补只限制后两个。1.7 中的 id来自表单配置,可能是用于查找媒体表单的路径式标识,照搬限制会影响这项合法用途。新增回归测试因此专门要求这个配置值保持原样。
同一补丁的上游测试用例覆盖了路径分隔符、上级目录片段、流式路径、编码、超长值和控制字符,也保留正常编号、逗号和长度 64 的合法值。在自己的隔离副本上检查修复时,应先让正常请求经过这条表单路径,再提交异常编号,观察是否被拒绝、文件是否留在预定目录内。如果请求被前面的配置检查挡住,就还没有测到这次修改。
漏洞资料里还有一处容易串线。原始安全公告围绕 __form-flash-id 进入 session_id 的路径展开,描述任意目录和 index.yaml 写入;九月事件采访讨论的是 __unique_form_id__ 进入 unique_id 的路径。它们最终碰到同一类目录拼接问题,入口参数和调用条件各自不同。检测只认原公告中的一个参数名,就可能漏掉后来公开的另一条路径。
4 改了首页之后,究竟拿走了什么
记者直接观察过新上传的文件和改掉的首页,Grav 维护者也确认了提交给他们的漏洞描述。ShinyHunters 还声称拿到日志、源码和 onion 服务私钥,并以所谓付款资料施压;记者没有独立验证这些失窃内容。
Clop 承认网站的软件没有完全更新,后来换了新的 onion 地址,同时否认重要的财务或运营资料失窃,也否认正在谈判。服务器上原来保存过什么、谁读到了什么,还缺镜像、日志和可核验的文件证据;公开材料也没有确认赎金已经支付。
5 站点恢复使用前
站点恢复使用前,先核对实际运行的 Grav 核心和 FormFlash 文件,别漏掉仍在接收请求的旧容器或副本。完成前述修复检查后,再走一遍访客真正会用的流程,提交咨询、上传并取回附件;使用自定义媒体表单的,也检查它是否还能工作。
如果漏洞入口曾经对外开放,还要回看更新前的文件变化。保留网站和反向代理日志、文件时间与可信备份,重点找临时目录之外的新文件,以及页面、插件和配置的异常修改。日志未记录请求体时,提交的表单编号可能根本没留下来,搜不到相关参数无法排除入侵。
确认或高度怀疑站点被写入后,从可信程序包重建,检查将要恢复的内容和配置,避免把可疑文件带回去。同时按被入侵进程可接触的范围处置凭据、私钥和关联系统,包括必要的撤销与轮换。临时访问限制要等这些检查和必要的恢复完成后再撤掉。
6证据与来源
6.1来源与材料
- BleepingComputer:9 月 19 日现场观察与 9 月 21 日更新https://www.bleepingcomputer.com/news/security/shinyhunters-hacks-clop-leak-site-threatens-to-extort-ransomware-gang/
- BleepingComputer:9 月 25 日漏洞路径与维护者采访https://www.bleepingcomputer.com/news/security/shinyhunters-hacked-clop-leak-site-using-grav-cms-path-traversal-flaw/
- Grav:CVE-2026-42608 / GHSA-hmcx-ch82-3fv2https://github.com/getgrav/grav/security/advisories/GHSA-hmcx-ch82-3fv2
- Grav:四月修复提交 d904efchttps://github.com/getgrav/grav/commit/d904efc33e03ebb597afde8d3368b28cf0423632
- Grav:1.7 分支回补与回归测试 a39fe54https://github.com/getgrav/grav/commit/a39fe54c6ffc6c20189bd8099a754a8f2c74daa6
- Grav:1.7.53.4 发布说明https://github.com/getgrav/grav/releases/tag/1.7.53.4
- Grav:当前稳定版 2.2.3 发布说明https://github.com/getgrav/grav/releases/tag/2.2.3
- Grav:官方当前下载建议https://getgrav.org/downloads