免责声明
本文由 LLM 参与创作。
本文内容仅供学习与研究使用,基于 GitLab 官方已公开的补丁与安全公告整理,
目的在于帮助自建 GitLab 实例的运维与安全研究人员理解漏洞成因、评估自身风险。
文中不提供可直接利用的攻击工具,所有验证均在本地隔离环境中完成。
请勿将本文内容用于任何未授权的测试或非法用途,由此产生的一切后果由使用者自行承担。

图 1 · 漏洞概览卡:CVE-2026-85706 基本信息与披露节奏
0x00 引言
九月初的 GitLab 安全更新里,有一个 CVSS 10.0 的洞格外扎眼:仓库 commits API 存在路径穿越,未授权用户可读取服务器任意文件。
这已经是 GitLab「未授权读文件」家族的不知道第几位成员了。比较有意思的是这次的两个根因叠得很典型——一个端点漏了鉴权,同时它又直接信任了客户端可控的文件路径,两件事凑一起刚好构成一条完整的攻击链。
更值得关注的是时间线:09-10 发补丁,09-11 WatchTowr 就观测到在野探测,之后进了 CISA 的 KEV 目录。补丁本身就是最好的漏洞说明书,这次也不例外,diff 一出来技术细节基本就藏不住了。
这篇博客就来完整过一遍这个洞:补丁怎么定位、根因在哪、官方怎么修的,以及自建实例该怎么排查。
0x01 漏洞概览
| 项 | 值 |
|---|---|
| CVE | CVE-2026-85706 |
| 产品 | GitLab CE / EE(自建实例) |
| 类型 | 路径穿越 + 鉴权缺失 → 未授权任意文件读取 |
| CWE | CWE-22 路径穿越 |
| CVSS | 10.0 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N |
| 影响版本 | 18.7 ≤ v < 19.1.8;19.2 ≤ v < 19.2.6;19.3 ≤ v < 19.3.2 |
| 修复版本 | 19.1.8 / 19.2.6 / 19.3.2 |
| 时间线 | 09-10 发布补丁 → 09-11 观测到在野探测 → 随后收录 CISA KEV |
| 报告者 | s3ntago(HackerOne) |
两点补充说明:
- GitLab.com 与 GitLab Dedicated 不受影响,官方在公开披露前就已修复,风险仅限自建实例。
- 漏洞本身影响面是机密性(读文件),不直接导致完整性/可用性破坏。但读到的可能是
secrets.yml,那就是升级到 RCE 的前置条件了。
官方公告:https://docs.gitlab.com/releases/patches/patch-release-gitlab-19-3-2-released/
0x02 定位修复补丁
GitLab 的补丁发布流程比较规范,安全修复会单独走 security-* 前缀的分支,这给定位省了不少事。
# commit: 0ff7b6b291
# "Fix unauthenticated arbitrary local file read in commits API"
# branch: security-627748-workhorsefile-lfi-19-3
拿 v19.3.1-ee 和 v19.3.2-ee 做 compare,68 个 commit、129 个 diff 看着挺唬人,但真正跟这个洞相关的只有 3 个源文件,其余都是常规的版本整理。
| 文件 | 改动 | 作用 |
|---|---|---|
lib/api/commits.rb |
+1 | POST :id/repository/commits 补上 authenticate! |
lib/api/files.rb |
+2 | POST/PUT :id/repository/files/:file_path 补上 authenticate! |
lib/api/helpers/commits_body_uploader_helper.rb |
+21/-11 | 路径来源从 params['file.path'] 改为 UploadedFile(核心) |
后一个文件是重头戏,前两个是顺手补上的鉴权缺口——注意 files.rb 也被改了,说明同一类问题在文件 API 上也存在。
0x03 根因分析
这个洞是两个根因叠加出来的,单独看任何一个都不至于这么严重,凑在一起才成了完整的利用链。
0x03.1 背景:Workhorse 为什么被设计成这样
要理解这两个根因,得先知道 GitLab 为什么会有 file.path 这套东西。
Workhorse 是 GitLab 的「智能反向代理」,用 Go 写的一层,跑在 Rails/Puma 前面,拦截所有进出 GitLab 的 HTTP 请求。它存在的理由很朴素:Ruby 处理大流量 I/O 太吃力。文件上传下载、git 的 push/pull、归档下载这类「长请求」如果直接在 Rails 里处理,会长时间占住一个 Ruby 进程和大块内存——官方文档的说法是,Workhorse 和 Puma 的内存占用能差一个数量级。
所以分工是:
- Workhorse 负责 I/O:把大文件请求体缓冲到磁盘的临时文件,或直接对接对象存储、Gitaly;
- Rails 负责业务与鉴权:该不该放行、这个文件属于谁,由 Rails 说了算。
对文件上传,标准流程是:Workhorse 先把上传体写到磁盘,再把「临时文件路径」经可信的请求头交给 Rails,Rails 拿到路径后按需读取。file.path、file.size 这些参数,本来的设计意图是由 Workhorse 落盘后回填给 Rails 用——最终形态应该是一个 Rack 解析出来的 UploadedFile 对象,它的 .path 指向那个临时文件。
这个设计本身没问题。真正的毛病出在下面两点:路径来源被写错了(信了客户端 body 里可伪造的 file.path,而不是 UploadedFile.path),以及压根没做用户鉴权。
0x03.2 根因一:鉴权缺失
# lib/api/commits.rb 修复前
post ':id/repository/commits' do
require_gitlab_workhorse!
# ❌ 缺 authenticate! —— 压根没做用户鉴权
attrs = file_params_from_body_upload
这里最容易踩的坑是 require_gitlab_workhorse! 到底管什么。它只校验「请求是经由 Workhorse 反向代理转发进来的」(验 Workhorse 的签名),而 Workhorse 对未登录的请求同样会加签名。
换句话说:
Workhorse 签名 ≠ 用户鉴权
另外那一行 route_setting :authorization, permissions: :create_commit 也很有迷惑性——它只是声明这个端点需要什么权限,供文档和中间件参考,真正的拦截靠的是显式的 authenticate!,而这里恰好漏了。
0x03.3 根因二:信任客户端可控的 file.path
# commits_body_uploader_helper.rb 修复前
def file_params_from_body_upload
file_path = params['file.path'] # ❌ 直接信客户端参数
bad_request!('local file not present') unless File.exist?(file_path)
check_large_request_rate_limit!(params['file.size'])
media_type = Rack::MediaType.type(params['Content-Type'])
if media_type == 'multipart/form-data'
env = { 'rack.input' => File.open(file_path) } # ❌ 打开任意路径
elsif media_type == 'application/x-www-form-urlencoded'
Rack::Utils.parse_nested_query(File.read(file_path)) # ❌ 读任意路径
elsif media_type == 'application/json'
Oj.load_file(file_path) # ❌ 读任意路径
end
end
file.path 完全由请求方控制,既没有路径白名单,也没有 File.basename 或穿越字符清理,拿到就直接往 File.open / File.read / Oj.load_file 里塞。
而且那行 File.exist? 检查还额外送了个侧信道——报错信息 local file not present 直接暴露了目标文件在不在。
0x04 攻击链
把两个根因串起来,完整链路是这样的:

图 2 · 攻击链:两个根因叠加才构成完整的未授权读取链
POST /api/v4/projects/:id/repository/commits HTTP/1.1
Host: gitlab.example.com
Content-Type: multipart/form-data; boundary=----x
------x
Content-Disposition: form-data; name="file.path"
/var/opt/gitlab/gitlab-rails/etc/secrets.yml
------x--
- 构造一个不带任何 token 的
POST /api/v4/projects/:id/repository/commits - body 里带上
file.path=<任意本地路径>、file.size=<任意值>,并按需指定Content-Type - 请求经 Workhorse 转发并加上签名 →
require_gitlab_workhorse!顺利通过(它不验用户身份) - 旧代码直接把
file.path当成服务器本地路径采纳 File.read(file_path)把目标文件读进流程,交给parse_nested_query解析——文件内容不会直接回显,只有解析失败时,报错信息才会带出部分内容- 三个分支(multipart / urlencoded / json)都能读文件,只要挑一个
Content-Type走通就行
0x04.1 可读目标与读取特性
以 GitLab 服务账号的权限为界,file.path 理论上可以指向任意文件,但得先拎清这个洞的读取通道到底长什么样——它并不是「读文件 → 把内容原样回显给你」:
- 文件被读进内存后,是当成请求体来解析(urlencoded / multipart / JSON 三选一),解析结果本该是创建 commit 的参数;
- 只有解析或后续校验失败时,错误信息才会把「部分内容」带出来——具体是被解析器当作参数名、又恰好触发错误的那些片段;
- 所以能读到多少,取决于目标文件的内容格式:
- 内容里有非法
%编码 → 回显出错位置附近几个字节; - 内容里含
[/]触发参数类型冲突 → 回显一整段参数名; - 不含
&/=/%/[的普通文件(比如/etc/passwd)→ urlencoded 解析会「成功」,整段内容被塞成一个参数名静默进入流程,后续报错并不包含它,反而读不到东西。
- 内容里有非法
换句话说:它是借错误信息间接泄露片段,不是整文件下载,也未必「任意文件都能读出来」。这也是公开 PoC 用「探测不存在的文件」来判定漏洞、而不是直接读文件的原因——要稳定读到内容,得挑格式对得上的目标文件。
高价值目标仍是密钥文件(secrets.yml / gitlab-secrets.json),但得现实一点:GitLab 的加密主密钥是完整随机密钥,AES 解密需要完整值,靠「片段」是解不密的。真正的危害在于——一旦借某个错误路径拿到了完整密钥(或足够长的连续内容),就能解密 CI/CD 变量、接管 Runner。能不能拿到完整值,需要针对具体目标实测。
官方修复时用了个 SECRET-CANARY-627748 文件做验证:修复前 canary 内容会被读出,修复后请求返回 401 且 canary 不泄露。
0x05 官方修复方案
官方的修复思路很清晰,两条线同时下手。
# 修复后:三处端点补上鉴权
post ':id/repository/commits' do
require_gitlab_workhorse!
authenticate! # ✅ 新增
...
# 修复后:只信 middleware 定稿的 UploadedFile
def file_params_from_body_upload
uploaded_file = params[:file]
bad_request!('file is invalid') unless uploaded_file.is_a?(::UploadedFile) # ✅ 类型校验
file_path = uploaded_file.path # ✅ 指向 Rack 解析 multipart 后的真实 tempfile
bad_request!('local file not present') unless file_path.present? && File.exist?(file_path)
check_large_request_rate_limit!(uploaded_file.size) # ✅ size 也从 UploadedFile 取
- 鉴权前置 —— 新增的
workhorse_authorize_commits_body_upload!注释里写得明明白白:*”Authenticate before Workhorse buffers the request body to disk”*。在 Workhorse 把请求体落盘之前就把鉴权做掉,既堵住未授权访问,也避免无谓的磁盘写入。 - 路径只信 middleware ——
UploadedFile.path是 Rack 解析 multipart 之后生成的缓冲 tempfile,是个服务端可控的路径,不再是客户端能随便指的字符串。同时size也改从UploadedFile取,避免并发修改带来的 TOCTOU 隐患。
0x06 检测与响应
0x06.1 日志排查
重点看 commits API 上带了 file.path 的 POST 请求,尤其是没带 token 的那些:
# 从 GitLab 日志里捞可疑请求
POST /api/v4/projects/{id}/repository/commits/ (body 含 file.path)
自建实例的 production.log / Workhorse 访问日志里都能找到痕迹。
0x06.2 处置建议
- 升级到 19.3.2 / 19.2.6 / 19.1.8。注意这次带 DB 迁移,单节点升级会有停机窗口,记得排期。
- 如果怀疑已经被打过,按凭据泄露处置:轮换 CI/CD 变量、各类 token、部署密钥,以及
secrets.yml里的加密主密钥和相关凭据。
0x07 GitLab 读文件漏洞家族
前面提到这是「家族」里不知道第几位成员。真把这个家族的成员摊开来看,会发现这支血脉比印象里长得多 —— 从 2013 年一直延续到现在,有 CVE 编号的就有三十多个。
为了不凭印象说话,我把 NVD 上 GitLab 的记录全量拉了下来(按 cpe:2.3:a:gitlab:gitlab 匹配,当时是 1400 余条),再按「路径穿越 / 任意文件读」的语义筛了一遍,得到下面这份年表。
0x07.1 完整年表

图 3 · 年份分布:2020 年单年 11 个,是全家族峰值
| CVE | 年份 | 触发点 | 后果 | CVSS |
|---|---|---|---|---|
| CVE-2013-4582 | 2013 | gitlab-shell(create_tag / import_project 等) | 本地文件信息写入仓库元数据 | 6.5 |
| CVE-2016-9086 | 2016 | import/export project(tar 内符号链接) | 读服务账号可访问的任意文件 | 6.5 |
| CVE-2017-0918 | 2017 | CI runner | 路径穿越 → RCE | 8.8 |
| CVE-2018-14364 | 2018 | projects import | 目录穿越 + 写 → RCE | 9.8 |
| CVE-2018-19572 | 2018 | GitLab Pages chroot | 符号链接 TOCTOU 越权读文件 | 5.9 |
| CVE-2018-19856 | 2018 | Templates API | 目录穿越 | 7.5 |
| CVE-2018-20229 | 2018 | 未细公开 | 目录穿越 | 7.5 |
| CVE-2019-6240 | 2019 | 未细公开 | 目录穿越 | 7.5 |
| CVE-2019-6783 | 2019 | GitLab Pages | 目录穿越 → RCE | 8.8 |
| CVE-2019-19088 | 2019 | 未细公开(EE 11.3~12.4.2) | 目录穿越 | 9.8 |
| CVE-2020-6833 | 2020 | Workhorse(请求走私) | 包与文件泄露 | 7.5 |
| CVE-2020-7966 | 2020 | 未细公开(EE 11.11~12.7.2) | 目录穿越 | 7.5 |
| CVE-2020-10086 | 2020 | 某端点未细公开 | 任意文件读 | 5.3 |
| CVE-2020-10953 | 2020 | NPM 功能 | 路径穿越 | 7.5 |
| CVE-2020-10977 | 2020 | move issue | 读 secrets.yml → 升级 RCE | 5.5 |
| CVE-2020-11505 | 2020 | Workhorse(请求走私) | NuGet 包与文件泄露 | 7.5 |
| CVE-2020-11506 | 2020 | Workhorse(请求走私) | 构件上传与文件泄露 | 7.5 |
| CVE-2020-13298 | 2020 | Conan 包上传 | 参数校验不足 → 有限文件泄露 | 7.2 |
| CVE-2020-13355 | 2020 | LFS 上传 | 路径穿越覆盖指定文件 | 7.5 |
| CVE-2020-13356 | 2020 | 绕过 Multipart 保护 | 读指定路径文件 | 8.2 |
| CVE-2020-26405 | 2020 | 包上传 | 路径穿越写任意位置 | 7.1 |
| CVE-2021-22190 | 2021 | Workhorse | 路径穿越泄露 JWT | 8.5 |
| CVE-2021-22201 | 2021 | 特制导入文件 | 读服务器文件 | 9.6 |
| CVE-2021-22203 | 2021 | Wiki 页面 | 任意文件读 | 7.5 |
| CVE-2021-22234 | 2021 | design image | 任意文件读 | 9.6 |
| CVE-2022-0244 | 2022 | group 导入 | 任意文件读 | 8.6 |
| CVE-2022-2531 | 2022 | Grafana API | 认证缺失 + 路径穿越 | 5.3 |
| CVE-2022-3067 | 2022 | Import 功能 | 读任意项目内容 | 6.5 |
| CVE-2023-2825 | 2023 | uploads 静态文件服务 | 未授权读任意文件 | 10.0 |
| CVE-2024-2434 | 2024 | 未细公开(16.9~16.11) | 路径穿越 → DoS + 受限文件读 | 8.5 |
| CVE-2025-1754 | 2025 | 公开项目 API | 未授权上传任意文件 | 5.3 |
| CVE-2026-9204 | 2026 | 仓库导入(次 URL 校验不足) | 认证用户读 Gitaly 任意文件 | 5.3 |
| CVE-2026-10053 | 2026 | 包注册表 | 路径穿越 → RCE | 8.5 |
| CVE-2026-85706 | 2026 | repository commits API | 未授权读任意文件 | 10.0 |
0x07.2 按触发点归类

图 4 · 家族时间线:34 个成员按年份铺开,颜色代表 CVSS 严重度
把上面这些按「出问题的位置」归类,聚集得很明显:
上传、静态文件与包管理(9 个)
CVE-2018-19572、CVE-2019-6783、CVE-2023-2825、CVE-2020-10953、CVE-2020-13298、CVE-2020-13355、CVE-2020-26405、CVE-2025-1754、CVE-2026-10053
这是数量最多的一类。uploads 静态文件服务、GitLab Pages、LFS、NPM、Conan、包注册表——共同点都是「文件名/路径由客户端提供,服务端直接拼接到存储路径上」。2023 年的 2825 和 2026 年的 10053 分别踩在 uploads 和包注册表上,说明这条路一直没被走完。
导入 / 导出 / 移动(7 个)
CVE-2016-9086、CVE-2018-14364、CVE-2020-10977、CVE-2021-22201、CVE-2022-0244、CVE-2022-3067、CVE-2026-9204
归档解析是重灾区。CVE-2016-9086 是 tar 包里的符号链接没校验,CVE-2021-22201 是「特制导入文件」直接读到服务器文件(CVSS 9.6),CVE-2026-9204 则是仓库导入时的次 URL 校验不足。只要有个功能需要解开用户提供的压缩包或从外部拉取,路径就要重新审一遍。
Workhorse 与请求处理(5 个)
CVE-2020-6833、CVE-2020-11505、CVE-2020-11506、CVE-2020-13356、CVE-2021-22190
这一组都跟 Workhorse 这层反向代理有关:三个是请求走私导致的文件泄露,CVE-2021-22190 是 Workhorse 里的路径穿越直接泄了 JWT。这也解释了本文漏洞为什么能绕过鉴权——只要把请求交给 Workhorse 处理,鉴权就得在应用层自己补上。
渲染器与解析器(3 个)
CVE-2017-0918(CI runner)、CVE-2021-22203(Wiki 页面)、CVE-2021-22234(design image)
API 的文件路径参数(2 个)
CVE-2018-19856(Templates API)、CVE-2026-85706(commits API,本文主角)
数量少,但含金量高——两个都能未授权直接读文件。
gitlab-shell 与仓库操作(1 个)
CVE-2013-4582——家族里最老的一位,create_tag / import_project 等函数会把本地文件内容带进仓库元数据。
端点未细公开(7 个)
CVE-2018-20229、CVE-2019-6240、CVE-2019-19088、CVE-2020-7966、CVE-2020-10086、CVE-2022-2531、CVE-2024-2434
官方公告只写了「allows Directory Traversal」这类一句话描述,没公开具体端点。按 90 天披露惯例,这些细节当年也是压着的。
0x07.3 十三年的规律
时间上有一个极其突出的峰值。
把 34 个成员按年份排一下:
2013 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026
1 1 1 4 3 11 4 3 1 1 1 3
2020 年一年就占了 11 个,比前后任何一年都多出一大截。2018 ~ 2021 构成第一波高发期(4 / 3 / 11 / 4),之后 2022 ~ 2025 明显缓了下来(3 / 1 / 1 / 1),到 2026 年又回到 3 个。
这里有个反直觉的点:2023 年只出了 1 个,就是那个 CVSS 10.0 的 2825。数量少不代表动静小,单条漏洞的影响完全可以盖过一整年的小漏洞。
触发点在迁移。
2013 gitlab-shell
2018 uploads 静态文件服务、GitLab Pages
2020 Workhorse 绕过、包管理(NPM / Conan / LFS)
2021 渲染器(Wiki)、导入文件
2022 导入功能、Grafana API
2023 uploads(2825)
2026 包注册表、commits API
这条线基本跟着 GitLab 的功能走:哪块新功能铺开,哪块就先出事。包管理这块尤其典型——2020 年就在 NPM、Conan 和包上传上连栽三个(CVE-2020-10953、CVE-2020-13298、CVE-2020-26405),2026 年的 CVE-2026-10053 又在包注册表上重演了一遍。
而本文的 commits API 属于「API 层」这一波。整份年表里 API 直接出问题的只有两条——2018 年的 Templates API(CVE-2018-19856)和本文的 CVE-2026-85706——但两条都是未授权就能读文件。这条路走得少,含金量却最高。

图 5 · CVSS 严重度构成:9 分以上 6 个,两个满分
满分的公式很固定。
两个 10.0(2825、85706)的共同点完全一致:未授权(PR:N)+ 能读到 secrets.yml 这类凭据文件。CVSS 向量也几乎同一串:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N
只要凑齐「不用登录」加「能摸到主密钥」,评分就直接顶格——因为读 secrets.yml 等于拿下所有加密数据,实际危害远超单纯的读文件。
失误就三种,反复出现。
- 路径拼接不做清理——不调用
File.basename、没有路径白名单,..直接生效。这是绝大多数成员的病根。 - 鉴权只声明不执行——
route_setting :authorization之类的声明写了,但真正的authenticate!漏了,或者被require_gitlab_workhorse!这类「看起来像鉴权」的校验顶替了。 - 信任客户端可控的值——路径、size、Content-Type 直接拿来用,不做服务端二次确认。
0x07.4 本文漏洞在这个家族里的位置
CVE-2026-85706 之所以值得单独写一篇,是因为它把上面第 2 条和第 3 条同时踩中了:
commitsAPI 漏了authenticate!(第 2 条:鉴权缺失)- 又直接信任客户端传进来的
file.path(第 3 条:信任可控值)
单看每一条,在家族史里都算「常规操作」;但两条叠在同一个端点上,就凑出了第 4 个满分成员。
0x08 PoC 现状与复现思路
截至本文整理时,公开 PoC 已经出现了(Rubby2001/CVE-POCS 下的 CVE-2026-85706),但它是「非破坏性存在检测」,不是完整利用。
这个 PoC 的思路很克制,也很值得防御方一看:
- 探测路径故意指向一个不存在的文件(如
/tmp/.cve-2026-85706-nonexistent-probe),只凭响应差异判定漏洞是否存在,不读取、不显示、不外传任何真实文件内容。 - 判据就一句话:
400且 body 出现local file not present→ VULNERABLE(服务端信了客户端file.path)401→ PATCHED(authenticate!已生效)403+ workhorse 字样 → NOT REACHABLE(请求没走 Workhorse 代理路径,检查反向代理配置)404→ 项目 ID 不对,换个真实存在的--project-id
- 请求里带了
Gitlab-Workhorse: yes头,确保走标准的 Workhorse → Rails 路径。
纯从学习角度复现,比之前更简单了:
# 1. 起一个 19.3.1 的 docker 实例
# 2. 用公开 PoC 跑一遍存在性检测,核对判定矩阵各分支
# 3. 想观察实际读取效果,把探测路径换成实验环境里的已知文件(仅限自有授权环境)
参考 CVE-2023-2825 的 ..%2F 多层编码绕过思路,大概率能迁移——根因是同一类。
0x09 小结
这个洞技术上不算复杂,但两个根因叠得很典型:
require_gitlab_workhorse!不等于鉴权。它只证明请求经过了 Workhorse,而 Workhorse 对着未登录请求照样签名。这个认知误区值得记一下。- 客户端可控的路径绝不能直接落进文件系统调用,哪怕做了
File.exist?检查也不行——那个检查本身还会变成侧信道。 - 「读文件」不等于「文件内容会回显给你」。这类洞的读取通道常常是借错误信息间接带出片段,能不能读到、能读多少,取决于目标文件格式;也别轻信「密钥碎片即可解密」的说法——完整密钥才解得动。
从防御角度看,最有价值的信息其实是时间线:补丁发布 → 次日在野探测 → 进 KEV。这个节奏下,自建 GitLab 实例的升级窗口基本只有一两天。这次带了 DB 迁移还没法热更,压力就更大了。
对自建实例的运维人员来说,结论很朴素:能升就赶紧升,怀疑被打过就按凭据泄露处理,该轮换的都轮换掉。
0x0A 写在最后:关于这份免责声明
再强调一遍开头那段免责声明——本文主要由 LLM 参与创作,从资料检索、GitLab 与 Rack 源码比对、公开 PoC 分析到初稿撰写,大模型都深度参与其中。
本文仅供学习与研究:内容基于 GitLab 已公开的补丁与安全公告整理,不含可直接利用的攻击工具,所有验证都在本地隔离环境完成。请勿将文中内容用于任何未授权的测试或非法用途。
坦白讲,这算是一次「AI 大模型时代下怎么做安全研究」的小小探索,这篇文章的写作几乎都由 LLM 完成:补丁 diff 能快速读完;十三年、三十多个成员的家族史能一次拉全;像「读取通道到底能带出多少内容」这种藏在解析器里的细节,也能顺着源码一路啃下去。
AI 能把这件事加速很多,但技术结论的最终确认,还是得回到源码和实测上。如果你也在用类似方式写技术文章,欢迎交流。