一通尚未接起的语音呼叫,铃声响起之前信令字节已经被客户端逐字段解码,解析器落在一条缺席的字段上

一、把“交互前解析”做成一条可判定的分类规则

公开披露给出的现象是:接听一通语音通话之后,手机界面退回主屏幕,应用图标出现掉落动画并显示被接管标识。整条路径没有要求用户点开附件,也没有要求用户输入任何东西,触发的是非法内存访问。现象本身先放一边,它逼出来的是终端安全盘点里最该有却没有的一条分类规则。

规则可以写成一句可判定的问句:一条远端数据,在用户点击、接听、打开文件这三个动作发生之前,是否已经被客户端解码,或者已经被按字段读取。答案只要有一个“是”,这段代码就归入“交互前就解析”那一类;全部为“否”,才归入“交互后才解析”。这条问句不依赖漏洞细节也不依赖具体协议,开发与测试可以对自己名下的每一条入站消息逐条回答一遍。

分完类之后真正变化的不是漏洞数量,而是控制模型。终端侧的安全控制大多把人当成最后一道闸门:培训员工识别可疑内容、在界面上给出提示、要求不要点开陌生链接。这套东西的作用点是“人做一次判断”。而“交互前就解析”这一类失效时,人在判断之前那段字节已经被处理完了,闸门根本没有被推过。于是风险模型里“需要用户配合”这个前提被整个拿掉,还能依赖的只剩代码自身的健壮性与版本是否修到位。

披露还给出两条容易被误读的边界:受影响的证据集中在安卓端,没有证据表明同产品的 iOS 端、桌面端应用或群组通话功能受影响;该缺陷尚未分配通用编号。前者是下面第三节的题目,后者是第四节的题目。

二、字段缺失不是少一个字段,而是判断与读取分了家

缺陷所在的那一类消息,负责上报麦克风静音状态、摄像头开关状态、视频旋转角度等信息。披露给出的形态很具体:当消息里带有旋转角度值时,代码先校验这个值,再去读视频状态字段,而这条消息里根本不存在这个字段。修复动作只是从正确的字段取值。

值得抄下来的不是字段名,而是这个结构:存在性判断与读取动作分家。代码并没有忘了检查,它确实检查了一个字段,只是检查的对象和读取的对象不是同一个。这类写法在解析层很常见:校验函数返回“这条消息格式合法”,紧接着另一段代码按自己的假设去取字段,中间只靠一个隐式约定连着。约定一旦与消息实际携带的内容脱节,读到的就是本不该读的内存区域。

按这个结构,必须补的用例形态是三种,而不是一种:必需字段齐全的正例;可选字段缺失的消息;字段顺序或承载位置发生变化的消息。只测第一种的项目最容易漏,因为在“都带齐”这条路径上,校验与读取的错配不会暴露,所有字段恰好都在,读哪个都对。第二和第三种才把“字段可能不在”这件事真正送进代码。

要在协议解析层把“字段不存在就走默认分支”变成可检查的约束,可行的做法是三件:取字段的入口只保留“带默认值的取法”,不给“假定存在”的取法留重载;解析完成后,把本次消息实际携带了哪些字段作为可读的中间结果保留,供断言与日志使用;把字段缺失列为模糊测试与用例生成的一等输入维度,而不是只在异常处理里写一句兜底。这三条都是断言层面的约束,不改协议也不加对外接口。

三、共用底层库的真实代价是多端修复时差

该产品的安卓、iOS、桌面端通话功能共用同一个通话底层库。披露里还有一条更值得记的线索:这个库的提交记录里有一项标注为“修复摄像头旋转”的合并,对同一问题做过同样的修正。同一次修正在同一个共享库上出现两次,而它仍然在其中一个端被真实触发,这就是“共享库修了不等于三端修了”最直白的样子。

另一类能力面(动态贴纸所依赖的动画渲染库)也是同样结构:产品方用另一套重写实现的同类库替换原库,三个端的替换完成时间各不相同,其中一个端替换之后仍保留原库。两件事合起来说明,一条缺陷的暴露面要用“能力面、端、版本区间、当前依据”四列来描述,用一列受影响版本号描述不了。

能力面 端 版本区间 当前依据
呼叫配置与状态同步消息的解析 安卓端 公告列出的首个受影响版本至末个受影响版本,修复版本已给出 厂商公告与版本发布记录
同一套解析逻辑(同源共享库) iOS 端 公告未列出受影响区间 无受影响证据,仍以该端版本清单复核为准
同一套解析逻辑(同源共享库) 桌面端 公告未列出受影响区间 同上,不以共享库的提交记录判定
群组通话路径上的同一解析逻辑 各端 公告未列出区间 无受影响证据,作为待复核项挂账
动态贴纸渲染 安卓端、iOS 端、桌面端 新旧两套实现并存的比例按端不同,一端替换后仍保留原实现 厂商公开说明与各端实际打包清单

这张表的用法落成一个核对动作:按端取版本下限,不按库的提交判定。判断“我们在用的这个端修没修”,唯一依据是该端发布分支里的版本号与厂商公告的修复版本比较;共享库的提交记录只能用来推断“哪个端可能受影响”,不能用来宣告修复完成。在洹拓的客户端交付评审里,每个能力面的每一端要拆成单独一行签字,任何一格写“共享库已修”都算未填写。

四、没有编号的缺陷靠什么定位,区间怎么推

台账侧的第一个误区是把通用编号当主键。编号未分配有三层含义必须分开:不表示未修复,披露里修复版本已经给出;不表示不严重,触发的仍是非法内存访问;也不表示可以不进台账,台账管的是“我名下有没有这个能力面、这个版本在不在区间里”,与外部有没有编号无关。没有编号时,一条缺陷要在企业内部台账里唯一定位,四件必填项缺一不可:

  • 厂商公告标识:厂商自己给出的通告编号、安全说明页地址或版本发布记录条目,用来在若干年后还能把这条记录对回原文;
  • 受影响版本区间:按端分别记录首个受影响版本与末个受影响版本,不写成一条跨端混排的范围;
  • 修复版本:同样按端记录,并且要能区分“该端尚未给出修复版本”与“该端未受影响”这两种完全不同的状态;
  • 内部判定状态:我们的资产里是否存在落在区间内的实例、由谁判定、判定依据取的是安装清单还是版本上报。

版本区间的推导要可复核,而不是拿发布年份的跨度当范围。定界只问两个问题:这段缺陷代码在哪个版本进入仓库;触发它的入口在哪个版本被启用。第一个问题定性质,第二个问题定起点。披露里明确,调用该消息处理逻辑的代码最早在首个受影响版本中启用,这也是列出的首个受影响版本。这就是“首个受影响版本由入口启用版本决定”的含义:代码存在于仓库里而没有任何路径调用它,这段时间对用户不构成暴露;入口一旦接上,暴露从这一刻起算,与代码是谁写的无关。

区间跨度大时老版本仍在网,原因通常是三条:终端不支持强制升级通道;企业客户批量部署的设备长期不换;备件与兼容清单把某个版本钉住。这三条决定推送修复的节奏,也决定我们在给客户做终端方案时,要不要把“能否强制升级”当成一项要交付的功能而不是默认成立的前提。

五、模型参与发现之后,变的是验收要求

另一条公开线索里,一个漏洞评审机构披露该产品各端用于渲染动态贴纸的动画渲染库存在一处未修复缺陷并给出评分。产品方先答复服务端校验机制可以缓解,另一个动作是向披露方索要完整的远程代码执行利用链。这个索要动作在工程上有一个明确读法:它把“这是一处内存缺陷”升级为“这是一处可以执行代码的缺陷”的证据责任,并把这份责任推回给披露方。另有研究采用由模型引导的模糊测试,向该库在三个端上的实现输入畸形数据,发现多处内存损坏并串联利用,且在公开演讲摘要中指出服务端校验机制可以被绕过。

本篇不复述任何构造过程,要的是当厂商只答“服务端校验可缓解”时追问的三件事。第一,校验在链路的哪一段:消息进入客户端之前、进入解析层之后,还是只在业务层做一次合法性判断。第二,绕过成立的前提是什么:它是否依赖某条消息由另一台可信服务代发,前提不成立时这条缓解是否随之失效。第三,服务端不可用时客户端是否仍然安全:离线、降级、直连本就属于客户端职责。三问都答不上来,“服务端可缓解”这句话不能作为把它降级处理的依据。

由模型辅助发现缺陷这件事,改变的是验收要求而不是结论。它不证明这类缺陷变多了,也不证明某个端的实现更差,它说明的是输入构造的边际成本下降之后,“我们只测过正常报文”这一条自证已经不足以支撑一个安全结论。验收要往上挪一格,从“有没有测过”挪到“字段缺失与畸形输入有没有被列为必测项、由谁判定通过”。这是要求的变化,不是对某个产品安全管理水平的评判。

六、使用方立场下能立刻做的三件事

上面这些,对洹拓这类不是厂商、而是给客户提供软件与终端交付的一方来说,落在三件具体的事上。

  1. 给通话与富媒体渲染这类“自动解析”能力设一个强制版本下限并纳入终端准入。这类能力的共同点是用户不需要做动作,所以准入条件只能是版本而不是提示。下限按端设定,取厂商公告的修复版本;低于下限的设备必须升级,否则不接入相关能力。
  2. 按业务需要显式关闭非必要的能力面。多数客户业务并不需要动态贴纸,也不需要摄像头旋转上报。能力面关掉,暴露面按整条消失计算,收益比任何加固都直接。我们习惯把这一条做成开通清单上的一行。
  3. 把“厂商未给编号”也纳入内部告警。等编号出现再处理,等于把自家台账的完整性交给外部机构的时间表。正确做法是公告标识、受影响区间、修复版本三个字段先入台账,编号到位后补录,判定状态始终由我们的资产清单驱动。

一条“响铃之前就在解析远端字节”的路径,会把风险讨论从“用户会不会点错”搬到“代码在字段缺席时有没有走默认分支、修复有没有真的到端”。分类规则、用例形态、四列暴露面表、无编号台账字段,这几件能自查的事都不需要任何漏洞细节就能在自家代码里跑一遍;本篇涉及的修复与版本事实只按公开披露转述,判断以厂商公告与自家资产清单为准。