针对Besder网络摄像头的逆向分析和漏洞挖掘

来源:岁月联盟 编辑:猪蛋儿 时间:2020-01-29

这篇文章,我会对Besder IP20H1网络摄像头进行逆向分析和漏洞挖掘。
硬件方面,IP20H1有4个电线连接器,处理器仍然是一个HI3516,一种常见的IP摄像头SoC。
前期,我要做的就是捕获数据包,读取它们,之后再开始编写自己的客户端!但在此之前,我必须要做以下3件事:
1.获取所有端口号,源和目的地以及使用它们进行通信的人员的列表;
2.研究数据包的基本结构并弄清楚基本的格式;
3.查看客户端软件,以了解内部工作的线索。
逆向分析
逆向分析使用的应用程序是XMEye应用程序,XMEye是一款监控软件,配套ipc、Dvr等前端监控设备,通过设备的序列号以云方式登录,将实时的监控画面显示的Android移动设备上并对设备进行预览操作,这意味着摄像头内置了DDNS连接。我为在网络中编写规则以防止摄像头访问互联网感到非常高兴。
在获取的第一个数据包中,我发现了一个20字节的序列,由1个单字节0xFF,13个0x00、0xFA05和最后4个0x00组成。我将其称为发现广播数据包(Discovery Broadcast Packet ,DBP)。
另外,还有一个类似于0xFA05的标记,即0xFB05,可能是客户端,而0xFB05是摄像头。从该数据包中,我可以看到选择的格式是JSON。使用自定义协议将更加容易,因为它的反序列化工作很简单。
查看标头,我可以看到数据包含一个0x3E,它恰好是数据的确切大小,不含20字节的标头。无论是在GitHub上,还是在其他任何搜索引擎上,在搜索名称“ GetSafetyAbility”时,找不到任何东西。另外,该协议还从UDP切换为了TCP。
遍历其余数据包,可以看到TCP数据流部分,因此我设置了一个简单的Wireshark过滤器,将结果过滤得更有条理。我只需要TCP PSH数据包即可:
tcp.flags.push == 1
当我用谷歌来检索这个包中的一些字符串时,发现了一些有趣的信息。
PasteBin
Gist
Github
在PasteBin中,有一些日志,查看其中的一些字符串可以获取一些有用的信息。首先,这是某种Android客户端,甚至可能是我已经安装的客户端,列出了它发送的JSON消息和一堆参数。
Gist尽管只是发送/接收数据包,没有什么太有趣的,但是文件中却包含着哈希密码。
Github里有最有趣的信息,它准确地描述了标头的制作方式。我关心的重要函数就是从sendSocketData开始的,它包含我真正关心的getSendDataInBinary函数调用。这将获取标头数据,并将其放入20字节缓冲区中,查看函数本身可以解释所有问题。
查看代码中用法的一个示例,可以看到所有字节实际上都是在Little Endian中排序的。
现在我知道secondInt实际上是SessionID!但是,通过查看fourthInt,我仍然不知道它是什么,但是它的值被硬编码到每个单独的命令中,所以我可以猜测,不管它是什么,它都与命令名或其他内容相关。然后,摄像头向客户端发送之前的命令的响应。
此时,客户端尝试登录设备,但是我已将默认密码更改为“password”,无论该密码是什么,它很可能是空白密码。之后的响应应该是“失败”,我可以看到下次登录尝试使用了不同的密码,并且实际上成功了。
我注意到,有趣的是,当身份验证失败时,报告说它是一个DVR,而在成功时则报告为IPC。但是到目前为止,关于是什么控制了SessionID字段的信息还很少。我可能需要解决这个问题。
获取哈希值
我最终也得到了一些密码,但是密码是经过哈希处理的,因此我需要找出制造商的方法。首先要注意的是,在其他捕获中,“密码”字段始终是相同的,这意味着可能我并非每次都在处理随机盐值。如果他们确实使用盐值,则必须将其硬编码到摄像头本身中,但这不太可能。这个哈希值真正奇怪的地方是,它是“MD5”,但哈希本身只有8个字节,而MD5有16个字节。这意味着存在某种哈希协议,但它是自定义的,并基于MD5构建。
进行了一番搜索后,我还是找不到关于此哈希格式的任何信息。在尝试过CyberChef和hash calculator等工具后,都没有成功。最后,还是在别人的帮助下找到了相关的信息。
最后,我使用了一些哈希系统的源代码来编写自己的Crystal哈希。
# Code translated from https://github.com/haicen/DahuaHashCreator/blob/master/DahuaHash.py
require "digest/md5"
module Dahua
  def self.compress(bytes : Slice(UInt8)) : Bytes
    i = 0
    j = 0
    output = Bytes.new(8, 0)
    while i
再根据我的一套方法,确定密码哈希。
"" = tlJwpbo6
"password" = mF95aD4o
"abcdef" = vfMMASaj
"123456" = nTBCS19C
"asdfghjkl" = MajKjGGZ
"000000000000000000000000" = lJ84MHiF
现在来做一些测试,看看是否可以跳过协议的某些部分!例如,开始的UDP跳可能是可跳过的,可以编写一个简单的程序尝试直接进入TCP端口34567。
require "./dahua_hash"
require "json"
require "socket"
def make_login_header(json)
"/xff/x01/x00/x00/x00/x00/x00/x00/x18/x00/x00/x00/x00/x00/xe8/x03#{String.new(Bytes[json.size])}/x00/x00/x00"
end
json_login = JSON.build do |json|
json.object do
json.field "EncryptType", "MD5"
json.field "LoginType", "DVRIP-Xm030"
json.field "UserName", "admin"
json.field "PassWord", Dahua.digest("password")
end
end
socket = TCPSocket.new("192.168.11.109", 34567)
socket
工作过程如下:
SUCCESS!
[Done] exited with code=0 in 0.831 seconds
模糊测试
在处理源代码时,我需要进行某些类型的测试,以确定如何正确格式化数据。在此示例中,我可以登录到摄像头,然后发送命令,然后获得SessionID。
0 = 0x00000001
1 = 0x00000002
2 = 0x00000003

[1] [2] [3] [4] [5] [6] [7]  下一页