一个分号,三个文件,整个账号。6月25日,有研究者向ZDI报告:在Telegram Desktop上点击一个https://链接,就能读取用户机器上的三个文件,并把它们发送到攻击者控制的频道。这三个文件,就是账号的全部。
9月17日漏洞被修复。而推送给数亿用户的更新日志里,这条修复被描述为一次"渲染改进"。
技术细节10月3日公开,CVE编号10月7日分配——CVE-2026-107181,CVSS评分8.1——公开的概念验证代码本周放出。漏洞本身值得的注意力,更新日志一个字都没给。
单实例IPC:一个比多数编程语言都老的模式
Telegram Desktop注册了tg://这个URL协议。在操作系统里点击任何一处tg://链接,处理器会启动第二个进程,这个进程连接到正在运行的实例所监听的本地套接字,把URL转发过去。这就是单实例进程间通信,一个比很多正在读这段代码的语言都古老的模式。
问题出在序列化上。这套序列化是自制的:关键字、参数、分号作为终止符。形如OPEN:tg://x?a=1;。接收端在每个分号处切分,把每一段都当作一条命令来处理。
而当URL被嵌入时,分号从未被转义。研究者写下的那句话就是整个漏洞,也是我读过对注入最好的单句描述之一:URL"只是一个载体"——一旦数据跨入记录,数据内部的边界就不再由结构持有,而变成了文本里的普通字符。
于是tg://x?a=1;CMD:quit作为一个URL抵达,正在运行的实例却解析出了两条命令。
更麻烦的是那个"只是为构建流程读取文件"的辅助功能。它实际上变成了一个远程文件传输服务,只要你能说出一个路径。能行动的废弃代码不是废弃代码,它是一个没有对外公布的API。
剥掉品牌,这是最古老的注入类型
把Telegram的标识剥掉,骨架是教科书里最老的一类注入:一个序列化边界,数据被允许包含分隔符。CSV公式注入、邮件头里的CRLF、HTTP响应拆分、Shell分词、SQL拼接——每一个都是"载荷里含有分隔符,而没有人转义分隔符"。
我在更小的舞台上撞过同一类问题。给自己的编码智能体写安全带时,一个shell的case模式展开了$HOME,而智能体的命令里含有字面文本——模式与数据之间的边界悄悄移动了。
这个家族在当代的成员,是提示注入:分隔符是自然语言,IPC通道是模型的上下文窗口。含有指令的工具输出之所以奏效,原因和;CMD:quit完全一样。"边界不再由结构持有"——把"结构"换成"系统提示词",这句话可以直接写进2026年的一份智能体事故报告。
修复本身把教训写成了代码。Telegram的补丁(提交db3405699f,"移除遗留解释路径辅助函数",涉及11个文件,+126/−372)加入了EscapeTo7bit与EscapeFrom7bit,对每一个低于32、高于127、或等于%和;的字符做百分号编码——并且当非本地URL共用连接时,彻底丢弃本地文件路径。转义分隔符,杀掉废弃能力,两件都要做,不是二选一。
加密在静息状态下的真实含义
报告的后半部分更安静,而在我看来更糟。
Telegram会加密本地会话数据。设计是:数据加密密钥(DEK)包裹你的数据,密钥加密密钥(KEK)包裹DEK,KEK由本地口令加盐派生。听起来没问题。
但默认情况下没有设置口令。于是KEK从一个空字符串派生——而盐以明文形式存放在tdata/key_datas里。所以这里的"静息加密"意味着:解密一切的那把钥匙,就存放在它所解密的东西旁边,外面什么都没包。被窃取的三个文件之一,就是这个文件。加密甚至没有拖慢攻击,研究者只是把会话连同挂锁一起拿走了。
这就是"看似合理的加密叙事"问题凝结成的一件实物。"会话在本地加密"是一句真话,却什么也不意味着——除非密钥派生函数绑定到用户真正选择的一个秘密上,否则它只是披着加密外衣的混淆。同一条规则我也用在日志上:一种你可以在不指出攻击者所缺秘密的情况下描述的保护,不是保护。
时间线与未说出口的部分
按研究者公布的时间线:
- 6月25日,向ZDI报告
- 9月17日,修复完成并推送
- 10月3日,技术细节公开
- 10月7日,CVE-2026-107181分配,CVSS 8.1
- 本周,公开概念验证代码放出
运行7.2.8版本的用户,从未被告知他们的更新与一个公开漏洞之间存在一场赛跑。静默的安全修复是一场赌注:赌整个已安装用户群在概念验证代码流传之前完成自动更新——而这场赌注押的是别人的账号。一行更新日志不花什么成本:"修复了一个构造链接可读取文件的漏洞。"厂商欠用户一份公告,研究者显然做了他的工作。
已确认的攻击链在Windows上。研究者在Windows上测试了6.9.3;tg://的单实例机制在macOS和Linux上同样存在,但报告没有确认这两个平台上的完整攻击链,我也没有在任何地方复现这次攻击——对运行中的即时通讯应用发起注入越过了我的底线,所以我的凭据是研究者的概念验证、报告中的文件与行号引用、以及已合并的修复提交,而不是我自己的运行结果。
厂商一侧的叙述只依赖一个信源。Telegram没有发布任何公告,所以"静默修复"中除公开提交之外的一切——ZDI的日期、那条渲染修复的更新日志——都来自研究者的报告。我把它写成时间线,而不是判决。提交本身会说话,围绕它的沉默也会。
你自制的序列化是什么?那个长出了逗号的CSV,那个长出了竖线的管道分隔日志,那个信任文件名的命令行工具。是哪个分隔符背叛了你——如果那些数据是别人的会话,代价会是多少?
热门跟贴