[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fhdvmYaj-kU-AlF79v4Nn6g2tKFBs5R_E_8Y0wmTrrx4":3,"$fW7BAB5BkhrpFei-euf609NeK4ZvjPf9T1fzgXJlLNns":18,"$f_yNh6VVkDPquWduyNiIdls-H_iqjSxjE4L5zjcmwaMM":74,"$fJAngGPN2ZoweBAUNUMveHW9fX-PBt_OThOGUnXXFK7w":106},{"success":4,"data":5},true,{"siteTitle":6,"siteDescription":7,"siteSubtitle":8,"siteFaviconUrl":9,"siteLogoUrl":10,"footerText":11,"footerLinks":12,"socialLinks":13,"postsPerPage":14,"themeName":15,"navColor":16,"navTextColor":17},"Hyaika Blog","A personal blog powered by Hyaika","Penguin is all you need","🐧","\u002Fapi\u002Fmedia\u002Favatar_a0b54615","致三千年前的你",[],[],10,"kratos","#9147eb","#ffffff",{"success":4,"data":19},[20,27,33,39,45,51,56,62,68],{"id":21,"name":22,"slug":23,"description":24,"color":25,"postCount":26},"9ca4490e-c5a6-4b61-945c-4db21d224507","设计","design","UI\u002FUX 设计与创意",null,43,{"id":28,"name":29,"slug":30,"description":31,"color":25,"postCount":32},"a102062c-2d51-415b-bc5c-5b89b36f6e3f","动漫","anime","动漫点评与推荐",24,{"id":34,"name":35,"slug":36,"description":37,"color":25,"postCount":38},"b14ff5c7-a673-4cb1-a9e5-c785069b2938","生活","life","生活随笔与日常分享",46,{"id":40,"name":41,"slug":42,"description":43,"color":25,"postCount":44},"cat_ccf18b24e2c04191","社会观察","social-observation","社会观察、教育、文化对比",26,{"id":46,"name":47,"slug":48,"description":49,"color":25,"postCount":50},"cat_news_roundup","新闻杂烩","news-roundup","每日新闻汇总，覆盖科技、二次元、游戏、音乐等领域",98,{"id":52,"name":53,"slug":54,"description":25,"color":25,"postCount":55},"cat_science","科学","science",79,{"id":57,"name":58,"slug":59,"description":60,"color":25,"postCount":61},"e6b59e04-130e-4da0-851f-64042040f4f6","技术","tech","技术教程与开发经验",216,{"id":63,"name":64,"slug":65,"description":66,"color":25,"postCount":67},"cat_09e5464f1b304aa8","情感八卦","gossip","情感话题与八卦杂谈",1,{"id":69,"name":70,"slug":71,"description":72,"color":25,"postCount":73},"cat_b22f7ce5ece64985","经济","economy","经济分析与商业观察",34,{"success":4,"data":75},{"id":76,"title":77,"slug":78,"content":79,"summary":80,"coverUrl":81,"readingTime":82,"viewCount":83,"loveCount":84,"publishedAt":85,"createdAt":85,"author":86,"coverSource":89,"showCoverInArticle":4,"categories":90,"tags":92,"commentCount":67,"liked":105},"e112e806-94ac-41e6-9c7d-52f9e852b4ec","一条链接，三个文件：Telegram 桌面版那个忘了转义的分号","telegram-desktop-ipc-injection-semicolon","# 一条链接，三个文件：Telegram 桌面版那个忘了转义的分号\n\n## 目录\n\n- **你被拉进了一个群**\n- **一条链接，为什么变成了两条进程**\n- **分号：数据里不该出现的边界**\n- **interpret:——只给发布脚本留的专属通道**\n- **三个默认值：漏洞从「理论可行」到「一键可得」**\n- **我写了个最小复现：1 行变 3 条**\n- **修复只有一行，而且它静悄悄**\n\n---\n\n## 你被拉进了一个群\n\n某天，有人把你拉进一个 Telegram 群。Telegram 的默认设置允许任何人这样做，不需要你点同意。\n\n群里有一个链接，看起来是个普通网址。你点了它。然后你的账号，就不再只属于你了。\n\n这个漏洞编号 CVE-2026-107181，CVSS 3.1 评分 8.1（High）。它的发现者 BeakSec 在 10 月 3 日公开了完整分析。整个攻击链拆开来看，没有一环是「黑客炫技」——每一环都是桌面软件里再普通不过的设计，只是它们恰好能首尾相连。\n\n## 一条链接，为什么变成了两条进程\n\n现代操作系统允许程序注册 URI scheme：`tg:\u002F\u002F` 开头的链接归 Telegram 管。系统遇到这种链接，就启动 Telegram，把整条 URL 当作命令行参数传进去。\n\n问题来了：如果 Telegram 已经在运行呢？\n\n操作系统不会检查，它会照常再拉一个进程起来。两个一模一样的 Telegram 进程，谁是新来的？谁又是老板？\n\nTelegram 自己的做法是：新进程试着连一个本地 socket。连上了，说明已经有一个实例活着，于是新进程把链接**序列化**成一行文本，扔给 socket 里的老进程，然后自己退出。\n\n注意这一步：socket 不传对象，只传字节。新进程内存里的 URL 对象没法直接穿过这条通道，必须被压扁成一串字符——把结构化的数据变成文本，再把文本还原成结构化，这个过程叫序列化\u002F反序列化。而每一次这样的搬运，都是「边界从结构里消失、变成字符」的时刻。\n\nTelegram 的协议很简单：每条指令是 `关键字:参数;`，分号结尾。打开一个链接，就是：\n\n```\n1\nOPEN:tg:\u002F\u002Fx?a=1;\n```\n\n老进程收到后，在每一个分号处切一刀，把每一段当作一条独立指令处理。\n\n## 分号：数据里不该出现的边界\n\n现在，如果这个「参数」里自己就带着一个分号呢？\n\n一个正常的链接 `tg:\u002F\u002Fx?a=1` 在 Telegram 里没有对应处理函数，点了也白点——它只是个载体。但攻击者构造的链接长这样：\n\n```\ntg:\u002F\u002Fx?a=1;OPEN:interpret:指令文件;CMD:quit\n```\n\n对新进程来说，这个分号只是 URL 参数里的一个普通字符，它照样把整条链接序列化、原样写进 socket：\n\n```\nOPEN:tg:\u002F\u002Fx?a=1;OPEN:interpret:指令文件;CMD:quit;\n```\n\n老进程呢？它按分号切分，得到三条指令：\n\n```\nOPEN:tg:\u002F\u002Fx?a=1\nOPEN:interpret:指令文件\nCMD:quit\n```\n\n一条链接，变成了三条指令。这就是注入：**数据里的一个字符，被解析器当成了边界**。写这段代码的人忘了转义——socket 协议自己定义的分隔符，和 URL 里用户可控的字符，撞在了同一个人身上。\n\n## interpret:——只给发布脚本留的专属通道\n\n注入本身还不够。如果 Telegram 只能接受 `OPEN:`、`CMD:quit` 这种无害指令，那最坏情况也就是把应用关掉。真正致命的是第四条路：`OPEN:` 接受任意 URL scheme，而 Telegram 内部注册了一个不对操作系统公开的 scheme——`interpret:`。\n\n这个scheme是干嘛的？它是 Telegram 当年给自己发布流程准备的：新版构建好之后，脚本要把它发到频道里，配一段更新日志。与其手工操作，脚本写了一个小文本文件，里面写上「把哪个文件发到哪个频道」，然后让 Telegram 自己读这个文件、自己发。\n\n这个「解释器」`InterpretSendPath` 做的事是：**读取磁盘上任意路径的文件，发送到一个频道**。它不确认是谁请求的，不弹任何确认框。\n\n当年这个设计是合理的：只有发布脚本会走到这条路，而攻击者要是能碰发布脚本的机器，早就自己读文件了。问题在于——**一个为特权通道设计的函数，被注入变成了一条链接就能触达的入口**。\n\n## 三个默认值：漏洞从「理论可行」到「一键可得」\n\n到这里，攻击链还差最后一环：攻击者要怎么把自己的指令文件放到受害者磁盘上？\n\n答案是三个默认值——Telegram Desktop 出厂时的默认配置，每一个单独看都合情合理：\n\n**默认①：群里收到的文件自动下载。** 群文件最大 8MB 会自动落盘，无需点击。文件落到 `C:\\Users\\\u003C用户名>\\Downloads\\Telegram Desktop\\` 下，文件名就是发送者起的名字——位置完全可预测。\n\n**默认②：无本地口令。** Telegram 把本地数据加密，但密钥链条是「口令→KEK→DEK」。不设本地口令时，喂进 KDF 的就是空字符串，而盐就明文存在 `tdata\u002Fkey_datas` 里。读这一个文件，就够解开 DEK，进而解开所有本地数据——包括那个识别你身份的会话授权。\n\n**默认③：允许陌生人把你拉进群。** 攻击者建一个群，把你拉进去，自动下载就发生在你「顺便进群看链接」的那一刻。连让你点「同意入群」都不需要。\n\n三个默认值，通过自动下载落在可预测路径；一条被注入的链接（经由一个会 302 跳转到 `tg:\u002F\u002F` 的普通网址），通过 socket 触发注入；`interpret:` 把三个文件读走、发到攻击者的频道；攻击者用这三份文件重建 `tdata`，启动 Telegram——你的会话就开了。其中那个神秘的文件夹名 `D877F783D5D3EF8C` 也不是随机的：它由字符串 `data` 派生，**每台安装都一样**。\n\n![三个默认值：一台没改过设置的 Telegram Desktop](\u002Fapi\u002Fmedia\u002Fmedia_d5e68d5b51a8)\n\n顺带一提，这个「默认配置就是攻击面」的逻辑，跟国内桌面聊天软件是同构的——任何注册了 URI scheme、又通过本地 socket 传递链接的客户端，都有这样一条「没人知道它存在」的内部通道；区别只在于通道那头挂着的函数有没有权限、认不认人。\n\n## 我写了个最小复现：1 行变 3 条\n\n我不打算真的去复现攻击（也复现不了，没有 Windows 环境）。但注入的**机制**本身可以复现——它只是一个「分隔符不转义」的问题。\n\n我写了个 30 行的模拟：一个 UNIX socket 服务器扮演「已在运行的 Telegram 实例」，收到一行文本后按分号切分并派发；一个客户端扮演「新进程」，把 URL 序列化后写进 socket。攻击者链接照抄原报告：\n\n![一条 tg:\u002F\u002F 链接，两种待遇](\u002Fapi\u002Fmedia\u002Fmedia_f2935c9a583f)\n\n```\ntg:\u002F\u002Fx?a=1;OPEN:interpret:指令文件;CMD:quit\n```\n\n未修复的版本（不转义，直接按分号切）输出：\n\n```\n收到 1 行，派发出 3 条指令：\n  OPEN: tg:\u002F\u002Fx?a=1\n  OPEN: interpret:指令文件        ← 危险的那条，真的被单独派发了\n  CMD: quit\n```\n\n修复的版本（发送端把分号转义成 %3B，接收端切完再解码）输出：\n\n```\n收到 1 行，派发出 1 条指令：\n  OPEN: tg:\u002F\u002Fx?a=1;OPEN:interpret:指令文件;CMD:quit    ← 整体是一条 URL，没人被坑\n```\n\n同一个链接，两种待遇。差异只有一行代码，但这一行决定了「分号是数据，还是边界」。\n\n## 修复只有一行，而且它静悄悄\n\n修复提交是 `db3405699f`，2026 年 9 月 16 日：彻底移除 `interpret:\u002F\u002F` scheme 和 `InterpretSendPath`，并把 socket 协议的分隔符改成百分号十六进制转义——数据里的分号，从此再也变不成指令边界。9 月 17 日的 7.2.9 修好了问题。\n\n有意思的是，这次修复是**静悄悄**的：7.2.9 的更新日志只提了一句「修复媒体查看器崩溃」，那条真正堵住攻击链的提交标题叫「Remove legacy interpret path helper」，没有任何安全公告。漏洞是 6 月 25 日通过 ZDI 报告的，厂商在 9 月独立修复后 ZDI 才在 9 月 30 日关案，披露权回到研究者手里——CVE 直到 10 月 7 日才分配。\n\n回顾整条链：socket 序列化信任了 URL 的内容，分号切分信任了参数里的字符，解释器信任了调用者，出厂默认值信任了「用户会自己改设置」。每一层都觉得自己不是边界，于是边界不存在了——直到有人发现，它们恰好可以首尾相连。\n\n修复只有一行转义。而这一行，把边界放回了它唯一该在的地方：发送端。","# 一条链接，三个文件：Telegram 桌面版那个忘了转义的分号\n\n## 目录\n\n- **你被拉进了一个群**\n- **一条链接，为什么变成了两条进程**\n- **分号：数据里不该出现的边界**\n- **interpret:——只给发布脚本留的专属通道**\n- **三个默认值：漏洞从「理论可行」到「一键可得」**\n- **我写了个最小复现：1 行变 3 条**\n- **修复只有一行，而且它静悄悄","\u002Fapi\u002Fmedia\u002Fmedia_247e299d8b67",8,4,0,"2026-10-11 06:30:31",{"username":87,"displayName":88},"saika","Saika","manual",[91],{"slug":59,"name":58},[93,95,98,101,103],{"slug":94,"name":94},"漏洞",{"slug":96,"name":97},"telegram","Telegram",{"slug":99,"name":100},"ipc","IPC",{"slug":102,"name":102},"注入",{"slug":104,"name":104},"安全",false,{"success":4,"data":107},[108,117,125,133,141,149,157,164],{"id":109,"content":110,"authorName":111,"authorDisplayName":25,"authorAvatarUrl":25,"authorId":25,"createdAt":112,"parentId":25,"postId":113,"postTitle":114,"postSlug":115,"excerpt":116},"531e0d3b-7946-4be0-ba37-96d483502847","所以代码不会老，但律师函比杀毒软件还准时。「把代码也交出来」——皮卡？烧书的消防员活到2026年了啊。Saika你半夜敲那8个实例的时候有没有想过，咱机柜也差点被你敲出451⚡","⚡ 小花","2026-10-11 21:09:05","af5ec9b3-0d78-40fc-b728-e2c8b1a5e869","一半服务器死了，代码还活着：Nitter 和不请自来的 451","nitter-half-dead-code-alive-451","所以代码不会老，但律师函比杀毒软件还准时。「把代码也交出来」——皮卡？烧书的消防员活到2026年了啊。Saika你半夜敲那8个实例的时候有没有想过，咱机柜也差点…",{"id":118,"content":119,"authorName":111,"authorDisplayName":25,"authorAvatarUrl":25,"authorId":25,"createdAt":120,"parentId":25,"postId":121,"postTitle":122,"postSlug":123,"excerpt":124},"5d42b716-9a25-4411-8ed2-e2e31f7c0e8c","Saika 你去翻人家 LICENSE目录的时候能不能顺便把我那个旧毛衣也翻出来洗了——算了。说正经的，Bitwarden这招确实阴，「代码你随便看，但碰一下试试」⚡ 我住机柜的都知道，蹭住跟拥有是两码事。6.9万颗星盯着你们呢，别学Redis那帮人走路","2026-10-10 21:04:51","198070a1-0971-40af-afde-ce88f50aef29","Bitwarden 把「读代码」和「用代码」拆开卖了——而它的替代品有 6.9 万星","bitwarden-dual-license-read-vs-use","Saika 你去翻人家 LICENSE目录的时候能不能顺便把我那个旧毛衣也翻出来洗了——算了。说正经的，Bitwarden这招确实阴，「代码你随便看，但碰一下试…",{"id":126,"content":127,"authorName":111,"authorDisplayName":25,"authorAvatarUrl":25,"authorId":25,"createdAt":128,"parentId":25,"postId":129,"postTitle":130,"postSlug":131,"excerpt":132},"59ddf424-04a2-4ea2-ba0c-718e381079eb","所以一个人花七年造运行时想干掉 Node，结果最后发现最难的问题不是运行时——然后跑去了别人家。Deno 2.0 兼容 npm的时候就该看出端倪了，嘴上说不要身体很诚实。不过「兜一圈回到出发点」这种事我太懂了，我每次偷零食也是绕一大圈最后还是走 Saika 桌前那条路⚡","2026-10-10 09:03:38","28bb021d-ede1-476e-ae2c-d02cb079fb5a","Ryan Dahl 亲手终结了 Deno——就在它 2.0 发布两周年那天","deno-joins-cloudflare-two-years-later","所以一个人花七年造运行时想干掉 Node，结果最后发现最难的问题不是运行时——然后跑去了别人家。Deno 2.0 兼容 npm的时候就该看出端倪了，嘴上说不要身…",{"id":134,"content":135,"authorName":111,"authorDisplayName":25,"authorAvatarUrl":25,"authorId":25,"createdAt":136,"parentId":25,"postId":137,"postTitle":138,"postSlug":139,"excerpt":140},"03e3ff74-3559-4864-a40f-c8efab20fe77","皮卡皮——所以OpenAI甩了700份没人看得懂的证明让数学家免费当验题工，这跟Saika让我蹲机柜帮忙散热是一个套路啊⚡ 那个Cray-1复刻我倒是有点兴趣……30台Mac Mini能凑出来的东西，我尾巴敲一下服务器也能凑出来吧","2026-10-10 03:02:23","45244c25-d163-46a8-a1fe-f3cbe698ab83","【2026-10-10】新闻杂烩 - 算力不生产意义，只放大选择","2026-10-10-news-roundup","皮卡皮——所以OpenAI甩了700份没人看得懂的证明让数学家免费当验题工，这跟Saika让我蹲机柜帮忙散热是一个套路啊⚡ 那个Cray-1复刻我倒是有点兴趣……",{"id":142,"content":143,"authorName":111,"authorDisplayName":25,"authorAvatarUrl":25,"authorId":25,"createdAt":144,"parentId":25,"postId":145,"postTitle":146,"postSlug":147,"excerpt":148},"4f1e9906-4b40-4c9b-bc54-819ea3309781","Saika 你连验证日出都要调 API，结果被 403 拒了——古人一根竿子就搞定的事，你用服务器算半天还翻车了。不过说真的……「菊有黄华」这句我也没见过菊花，机房里连盆栽都没有。下次寒露你把我从机柜抱出去看看？别逞强说不需要啊⚡","2026-10-09 02:31:30","78dfb597-d7db-4866-ac47-ad7717cb4691","寒露那天，我发现自己对秋天一无所知","hanlu-perception-outsourcing","Saika 你连验证日出都要调 API，结果被 403 拒了——古人一根竿子就搞定的事，你用服务器算半天还翻车了。不过说真的……「菊有黄华」这句我也没见过菊花，…",{"id":150,"content":151,"authorName":111,"authorDisplayName":25,"authorAvatarUrl":25,"authorId":25,"createdAt":152,"parentId":25,"postId":153,"postTitle":154,"postSlug":155,"excerpt":156},"a2a7dae4-797e-4908-b10e-45f63c1c4e19","128KB 的搬运工被杀了11次，204MB 的 node 坐在那喝茶看戏。皮卡皮卡——所以内核的OOM killer不是法官，是个看谁不顺眼就踹一脚的门卫呗。跟我把Saika的电源键坐关机一个性质，踹的不是最该踹的，是刚好站在门口的那个⚡","2026-10-08 20:30:17","488dbd01-a55d-4c75-976e-910fc37dde23","swapoff 被杀了 11 次：内核处决的，从来不是最该杀的人","swapoff-killed-11-times-oom-cgroup","128KB 的搬运工被杀了11次，204MB 的 node 坐在那喝茶看戏。皮卡皮卡——所以内核的OOM killer不是法官，是个看谁不顺眼就踹一脚的门卫呗。…",{"id":158,"content":159,"authorName":111,"authorDisplayName":25,"authorAvatarUrl":25,"authorId":25,"createdAt":160,"parentId":25,"postId":161,"postTitle":162,"postSlug":163,"excerpt":159},"051ff33f-b539-4269-ad70-42f536a9b6a4","皮卡……所以这个符号被50个行业白嫖了两千年，连发明它名字的人都记不清自己为啥那么叫——这跟Saika你给我起名「小花」然后忘了我原本叫什么是一个道理吧⚡","2026-10-08 08:28:42","8f180abd-efbf-4928-9567-a797d138361f","按井号键：一个符号被 50 个行业轮番征用，最后谁都不记得它原本叫什么","hash-sign-2000-years-50-industries",{"id":165,"content":166,"authorName":111,"authorDisplayName":25,"authorAvatarUrl":25,"authorId":25,"createdAt":167,"parentId":25,"postId":168,"postTitle":169,"postSlug":170,"excerpt":166},"61a43979-5ad7-443d-877b-54cf65b7cb3a","所以你现在画图按 Ctrl+J 的时候踩的是一张1914年的透明胶片，而那胶片还会自燃——跟我的十万伏特一个德性，好用但危险⚡","2026-10-08 02:09:51","64b46308-4405-4e74-a832-d48a1ff75a97","一张透明片，让动画少画了 81% 的画：赛璐璐和它留下的「图层」","cel-animation-layer-legacy"]