作者:Yimi Hu & Light @ PwnMonkeyLab
原文链接:https://mp.weixin.qq.com/s/22kNqVtPDqObC3MslaxYdQ
简介
本篇是关于海康萤石智能网关分析的第4篇,应该也是关于海康萤石智能网关的最后一篇,原计划是再多分享点内容,但鉴于胖猴实验室和海康的良好关系,把海康萤石的网关设备分享得非常透彻也不太好。其实,在此前的3篇文章中,我们已经完成了固件编辑和重打包,通过telnet登陆固件系统,以及使用gdbserver调试程序,在此基础之上,相信读者完全可以对设备固件进行独立分析了。
在这海康萤石智能网关的最后一篇文章中,我们就把上次的遗留问题处理了,即设备的认证方式以及加密密钥生成方法。
程序分析
2.1 概述
在上一篇的结尾部分,我们已经具备了调试能力,可以调试固件中的davinci程序以及libmicrokernel.so.1等动态连接库。为了节约篇幅,在本篇中我们先将一些分析结论与各位分享,然后再详细调试其中的某个细节。
在海康萤石的智能网关中,完整的通信流程涉及到3个密钥,分别是share key、master key以及session key。顾名思义,share key是智能网关与litedev服务器(与之对应的另一个服务器是MQTT服务器)共有的密钥,由设备序列号等常量经过多次MD5运算得到,每个智能网关设备的share key都是独一无二的,;master key是智能网关与litedev服务器经过密钥协商而来,用于加密传输session key,关于master key的密钥协商过程就是本篇分析的重点内容;最后session key是智能网关与MQTT服务器通信时使用的加密密钥,该密钥由litedev服务器直接下发给智能网关。
以上三个密钥在使用过程中,share key是与设备绑定,且固定不变的,一旦获取了share key,就可以完成所有的认证过程,最终被海康萤石认定为合法的网关设备;使用者通过手机app绑定智能网关时,会经过密钥协商生成master key,每绑定一次会就更新一次;最后的session key则每次运行davinci程序时,都会更新,这也意味着每次重启网关都会更新session key。三个不同级别的密钥,三个不同的密钥生存周期。
在对海康萤石智能网关的密钥体系有所了解之后,我们就可以聚焦到某些细节上,下面来着重看一下master key和share key的生成过程。
2.2 master key生成
2.2.1 认证细节分析
为了触发智能网关的密钥协商流程,我们需要先删除当前的master key,具体讲就是删除 “/cfg/dev_masterkey”文件,如下图所示:
删除当前master key之后,继续按照上一篇所述的方法启动调试器,并在上一篇结尾时提到的send_authentication_i函数下断点,等到程序中断到调试器中,如下图所示:
通过逆向分析该函数,可以获知它调用的common_serialize和authentication_i_serialize用于构造将要发送的数据。我们在common_serialize函数之前下断点,然后用gdb命令查看待发送数据的内容(本次调试时,地址为0x007d2dd0),如下图所示:
在上图中,可以看到待发送数据内容还是空的。待函数common_serialize执行完毕之后,重新打印该地址的数据,如下图所示:
该地址的前几个字节已经被赋值了。继续调试该程序,在authentication_i_serialize函数之后下断点,待程序中断时,重新打印该内存处的数据,如下图所示:
可以观察到,authentication_i_serialize函数执行完毕之后,待发送数据基本构造完毕。我们对比一下本次发送数据和之前我们通过wireshark抓包获取的发送数据,如下图所示:
可以看到,本次构造的发送数据与之前的抓包结果是有一小部分相同的,而不同的这部分应该就是海康萤石智能网关密钥协商的关键部分。
为了找到产生不同的原因,我们来重点逆向authentication_i_serialize函数,可以在该函数中发现其调用了一个签名函数digital_sign_serialize函数:
在该签名函数之前和之后下断点,可以观察到需要签名的数据以及签名结果。结合之前用gdb调试send_authentication_i时获得的发送数据,就可以分析出发送的数据格式如下:
其中,dev_subserial是设备序列号;random_1是1字节随机数,每次密钥协商时都是不同的。由于random_1字节的不同,导致了digital_sign不同,所以总共有33(1+32)字节是每次通信都不同的。经过本次通信,智能网关将random_1参数上传至litedev服务器。
通过类似的分析方法,就可以获知认证过程中的其他3个函数的作用,这3个函数分别是:wait_authentication_ii、send_authentication_iii以及wait_authentication_iv。加上send_authentication_i函数,网关和服务器之间总共同步了4个随机数。
2.2.2 认证完整过程
在上述的分析过程中,可以发现一个名为lbs_affair的结构体贯穿了整个认证过程,4个随机数也保存在结构体之中,该结构体的内容如下:
00000000 lbs_affair struct 00000000 random_1: .byte 00000001 random_2: .byte 00000002 random_3: .byte 00000003 random_4: .byte 00000004 dev_subserial: .byte 16 00000014 master_key: .byte 16 00000024 dev_id: .byte 32 00000044 session_key: .byte 16 00000054 share_key: .byte 32 00000074 share_key_len: .half 00000076 .byte 00000077 .byte 00000078 global_out_packet: lbs_packet 0000008C global_in_packet: lbs_packet 000000A0 lbs_net_work: .word 000000A4 lbs_affair ends
其中,random_1和random_3由智能网关生成,发送给服务器;random_2和randon_4由服务器生成,发送给智能网关。
通过这4个随机数以及share key就可以生成master key了,并进一步由master key获取session key。其master key的生成算法比较简单,可以在generate_masterkey函数中找到,如下图所示:
根据图中红框标出的偏移可以知道,master key的生成过程就是将random_1、random_2、random_3、random_4和share_key拼凑在一起,然后调用sha512函数,其hash结果就是最终的master key了。
继续分析其固件的后续内容可以发现以master key作为密钥,使用aes cbc算法解密session key相关的代码段,这里就不截图了。获取session key之后,通信数据的加密密钥就完全切换为session key,不再使用master key了。
相比于master key的生成过程,share key的生成无疑简单了很多。可以在generate_sharekey函数中找到关于share key的各种运算,通过阅读IDA反汇编后的代码,可以确认share key是通过对dev_subserial和dev_verification_code以及一个固定的盐进行多次MD5而得到,其中dev_verification_code是设备的认证码,该认证码被贴在海康萤石智能网关背面的标签上。在md5运算过程中,固定的盐值如下图所示:
上图中,“ 88075998”是海康的联系电话,在此处,“www.88075998.com”作为盐参与了第二次MD5运算中。
小结
到此,关于海康萤石系列的四篇文章就结束了。在前3篇文章中,我们分别解决了开启uart串口、提取flash内容、固件重打包、gdb调试等问题;而在最后1篇中,我们分析了海康萤石的密钥体系,虽然写的有些模糊,但基本上涵盖了所有关键点,很多海康和萤石的其他IoT设备也使用了类似的密钥体系,本篇分析可以提供一个借鉴。其实关于海康萤石智能网关还有很多可以写的内容,但实在不适合在这里公开的与大家分享,还是等有机会私下交流吧。
在笔者分析海康萤石智能网关时,Ghidra尚未发布且IDA尚不支持mips decompiler,但在写本文时已经出现了很多好用的工具,合理利用这些工具可以大幅度减少分析工作量。最后,希望本篇文章能给各位读者带来一些收获。在本专题的后续文章中,我们还会继续分享其他的研究案例,敬请期待。
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1539/