一提起智能家居传感器,很多人脑海里蹦出的关键词就是“不安全”。水表、门窗磁、温湿度计这些常年挂在犄角旮旯的小玩意,似乎天生就是被黑客随便拿捏的对象。但真拿一台畅销的家用水表传感器拆开通信链路,从头到尾走一遍破解流程以后,我得到的结论多少有点意外——这东西的安全设计,其实比想象中讲究得多。
这次被摆上解剖台的是一台 Flume 水表监测器。它算不上什么冷门设备,美国家庭用的很多,功能也直白:扣在自来水表上,隔几秒就把用量读数通过无线信号发到家里的一个桥接器,桥接器再经 Wi‑Fi 把数据送上云端,用户在手机 app 里就能看到实时用水量。重点是水表传感器到桥接器这一段,用的是一段 915 MHz 的射频链路,而且厂商标明是加密传输。这就勾起了我的兴趣:到底用的什么加密?强度够不够?能不能被逆向?花了大概几周的业余时间,配合安全研究员 Steve Crosby 此前公开发布的一些逆向分析数据,我最终成功解开了这条链路里的所有秘密。
第一步永远是先把无线信号从空气里揪出来。Flume 的 FCC 文档写得很清楚:传感器在 902.5–927 MHz 之间做跳频,一共 50 个信道,信道间隔 500 kHz。我直接把 LimeSDR Mini 软件无线电的中心频率摆到整个频段的正中间,打开 20 MHz 的显示带宽,然后拧开水龙头。几乎是立刻,屏幕上就窜出一个个持续时间大约 1 毫秒、带宽 500 kHz 的短促脉冲——每拧一次龙头,传感器就会发一条消息,信号特征干净得让人感动。为了看得更清楚,我把接收中心频率切到 909 MHz 附近,频谱上那个窄窄的尖峰就像有人定时在对讲机里按了一下按钮。
确定了信号的位置,接下来就要弄清楚它的调制方式。很快我就确认,这是最经典的 2‑FSK(二进制频移键控),符号速率 200 kbps。传感器内部用的是一枚很常见的 RFM69 收发芯片,这颗芯片支持硬件数据白化,也就是在发送前把原始比特序列跟一个固定伪随机序列做异或,免得连续 0 或连续 1 太多导致接收端同步出问题。White 序列的开头几个字节是 FF 87 B8 89……,后续完整序列虽然芯片数据手册语焉不详,但结合社区资料也拼了出来。整个物理层的帧结构直截了当:先是前导码和同步字帮接收端找准信号起始位置,紧接着是消息头、加密载荷,末尾用一个 16 位的 CRC 做完整性校验。
CRC 这部分我也没有放过。生成多项式用的是 0x1021,和芯片手册标注一致,但初始种子和异或输出值文档里没有直接给。好在这两个参数完全可以暴力穷举——试过所有可能组合后,再去比对实际抓到的几帧消息,很快 CRC 的计算公式就被完整还原了出来。这样一来,我不但能校验收到的消息是否损坏,还能自己伪造任意载荷并把 CRC 算对。
到了这一步,最棘手的只剩那 16 字节的真正载荷——全程由 AES‑128 的 ECB 模式加密,同样是 RFM69 芯片内置的能力。AES‑128 意味着密钥有 128 位,靠蛮力穷举是根本不可能的事。但 Steve Crosby 公布的原始数据里藏着一个关键发现:实际使用的 128 位密钥,是由一个仅 8 字节的主密钥,通过一个固定、硬编码的映射扩展而来的。也就是说,真实密钥的熵只有 64 位。64 位听起来还是大到不能直接硬破,可随后第二个信息缺口被堵上了:消息头里有一段并没有加密的字段,竟然额外泄露了密钥里的 16 位信息。这把待破解的空间从 64 位压缩到了 48 位。再结合 Crosby 数据里暗示的密钥最后一个字节有一半比特是固定常量,最终的未知比特数被进一步压缩到了 44 位。
44 位是什么概念?差不多 17 万亿种可能。对于一个个人项目来说,直接拿 CPU 硬算还是会算到地老天荒,但如果上 GPU 就完全不一样了。我写了一段并没有特意优化的 Python 脚本,同时喂给它多条捕获到的真实消息,让它在 2^44 个密钥空间里寻找那些解密后明文看起来“像”正常传感器数据的结果——更准确地说,是找那些具备低信息熵或者多帧之间相同比特比例异常高的解密输出。一块性能还不错的 GPU 跑了大约一天,云端租用成本不到 10 美元,就成功捞出了那枚唯一的正确密钥。
密钥到手之后,整条链路就再没有任何秘密了:我能解密任何一次传输的用水读数,也能随便构造跳频特性、CRC 和加密格式都完全合法的假消息,然后通过 SDR 发射出去。理论上,如果厂商在桥接器端支持并开启了 OTA(空中升级),这些伪造消息甚至有可能被用来注入恶意固件更新。但话说回来,这种攻击仍然需要攻击者待在 915 MHz 信号的覆盖范围内,同时还要骗过接收器的跳频同步,实操门槛并不算低。
回顾整条破解链条,真正让我觉得有意思的地方,不是它被攻破,而是作为一个消费级水表传感器,它居然至少在三个层面做了功课:物理层用了跳频来对抗干扰和简单的嗅探;链路层加了 CRC 保证数据不被悄无声息地篡改;载荷里实实在在地走了 AES‑128 加密,而且密钥并没有脆弱到几秒钟就能被撞开。最终有效安全强度落在 44 位,配合跳频的物理隔离效应,对于一个记录你家冲马桶频率的设备来说,这已经不是“形同虚设”了,甚至可以说,在同类产品里已经算得上挺认真了。
当然,这次逆向也再次提醒一个老问题:硬件里只要有一处信息泄漏(比如消息头顺手带出的那 16 位密钥),再厚的加密墙也会被削薄。对于 Flume 的用户来说,短期内没必要因为这条研究就冲去拆掉水表传感器,但保持桥接器固件更新、留意社区安全报告,总归是个好习惯。而对于做物联网硬件的团队,这个故事也许能提供一个更具体的参照——安全不是一次做对就一劳永逸,而是每一帧消息、每一个不经意留下的旁路,都在重新定义你的实际防线到底有多厚。
热门跟贴