物聯網設備安全分析之MAX! Cube LAN Gateway篇
前言
在這篇文章中,我們將為讀者介紹如何對物聯網設備進行安全評估。這里將會詳細介紹進行評估所需的基本方法:對于不同的任務需要使用哪些工具,以及如何解決在分析過程中可能出現的問題。本文的目標讀者對為對物聯網設備安全分析感興趣的朋友,對逆向工程感興趣的讀者,或者只想了解如何通過技術手段來處理未知設備的讀者。
本文的重點不在于揭示某種設備的某種漏洞,而在于闡釋影響各種IoT設備的安全弱點,因此,本文介紹的內容同樣適用于其他的設備和場景。
分析對象
本文的分析對象是來自eQ-3公司的 MAX! Cube LAN Gateway (以下稱為“Cube”)。實際上,許多產品都捆綁了該設備,比如我的加熱控制系統中就帶有該設備。通過該設備名稱中的“Cube”不難猜出,它只是一個LAN網關,通過RF技術實現真正的“物聯網設備”或“智能設備”之間的通信。在本文中,我們將重點介紹以太網通信,因為它是管理軟件的主要通信方式。
搭建中間人攻擊場景
為了全面地了解該設備的通信狀況,我搭建了一個簡單的中間人攻擊場景。我在自己的系統上使用了一個USB網卡,并將其直接連接到Cube。首先,打開Cube,但是不要使用任何管理客戶端或其他需要通信的軟件,這樣就能了解Cube自身發送了什么數據包。我們發現,它只是試圖通過DHCP獲得IP,然后開始解析ntp.homematic.com。
為了讓Cube可以訪問互聯網,我已將自己的USB網卡配置為Cube的路由器。為了在不使用DHCP的時候可以通過192.168.0.222訪問Cube,我把設備的IP地址設為192.168.0.1/24,并進行了如下所示的配置,以允許通過USB網卡的NAT訪問互聯網:
- sysctl net.ipv4.ip_forward=1
- iptables -t nat -A POSTROUTING -o enp0s25 -j MASQUERADE
- iptables -A FORWARD -i enp0s20u9u3 -o enp0s25 -j ACCEPT
- iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
注意:設備enp0s20u9u3是連接到Cube的USB網卡,enp0s25是系統上的另一個網卡,該網卡連接到具有互聯網連接的路由器上。
因為Cube會向默認的路由器發送DNS查詢,所以,我們可以在機器上設置一個DNS服務器,或者直接將DNS查詢轉發給相應的域名服務器(例如OpenDNS服務器):
- iptables -t nat -A PREROUTING -i enp0s20u9u3 -p udp --dport 53 -j DNAT --to 208.67.222.222:53
- iptables -t nat -A PREROUTING -i enp0s20u9u3 -p tcp --dport 53 -j DNAT --to 208.67.222.222:53
這樣一來,我們就能設法觀察Cube發送的所有的數據了,例如使用Wireshark或tcpdump等工具來嗅探連接到Cube的NIC上通信數據。
發現網絡中的Cube設備
為了方便管理員自動識別本地網絡上的Cube設備,Cube提供了相應的網絡發現功能。下面的Wireshark屏幕截圖展示了由本地管理軟件發送的數據包:
該軟件會向23272端口上的多播組地址224.0.01發送UDP數據包。在右下方,標記出來的hexdump部分是有效載荷(eQ3Max * \ x00 ********** I)。這是一個所謂的身份消息,用來命令Cube向數據包的源主機報告其序列號。下面的截圖顯示了Cube的響應:
同樣,這里響應的有效載荷也在右下側(eQ3MaxApKMD1016788> I)做了標記。 我的設備的序列號是KMD1016788,所以一切正常。現在,我們只要發送這樣的UDP數據包,就能輕松找出本地網絡上所有的Cube設備。同時,我們也可以檢查某個主機是否是使用單播數據包的Cube設備。
我們自己也可以發送所有這些數據包,甚至單播數據包。對于這些任務,我更喜歡使用Scapy來完成。因為有了它,我們就可以在交互式Python shell中創建、發送/接收和操作數據包,這樣做是很方便的,因為這樣可以同時進行其他任務,如進行計算或數據轉換。Cube使用的UDP端口是23272。我們可以從前面的示例中獲取相應字符串(這是一個“身份消息(identity message)”,由有效載荷末尾的“I”表示),并將其發送到目標主機:
- >>> p = Ether()/IP(src="192.168.0.1", dst="192.168.0.222")/UDP(sport=23272, dport=23272)/Raw("eQ3Max*\x00**********I")
- >>> sendp(p, iface="enp0s20u9u3")
響應的有效載荷的內容如下所示:
- 00000000 65 51 33 4d 61 78 41 70 4b 4d 44 31 30 31 36 37 eQ3MaxAp KMD10167
- 00000010 38 38 3e 49 00 09 9c 3e 01 13 88>I...> ..
注意:Cube發送的響應數據包的源和目標端口總是23272。所以,你要么一直使用23272作為源端口來獲得響應,要么使用pcap進行帶外捕獲——如果你使用隨機源端口的話。
實際上身份消息是非常有用的:所有其他UDP消息都需要該Cube的序列號,它將被放入請求的響應中。有了這個序列號,我們就可以發送其他消息類型了,例如 “重新啟動消息(reboot message)”(在有效載荷末尾用“R”進行標識):
- >>> p = Ether()/IP(src="192.168.0.1", dst="192.168.0.10")/UDP(sport=23272, dport=23272)/Raw("eQ3Max*\x00KMD1016788R")
- >>> sendp(p, iface="enp0s20u9u3")
就像該消息的名稱所示,它會重新啟動該設備。這種消息可以從本地網絡上的任何設備發出,無需任何身份驗證。
如何管理Cube
Cube的管理方式有三種:
本地管理軟件:這是一個可安裝在Windows上的EXE程序,它會在一個較大的隨機端口上啟動一個本地Web服務器,用于java applet…
遠程管理軟件:功能與登錄界面基本相同,只不過是托管在云中而已。
移動應用:我還沒有見過。
本地和遠程管理軟件的主要區別是遠程軟件的通信是加密的,這一點將在下文詳細介紹。
本地管理軟件非常有助于深入了解Cube的內部工作原理,因為通過觸發不同的功能,我們就可以觀察發送給Cube的相應請求了。為此,我們可以每次執行一個功能,然后捕獲相應的流量,從而大概了解哪些事情是可以通過Cube的遠程管理來執行的。
與“遠程代理”進行交互
為了使用遠程管理軟件,您必須在本地管理軟件中配置遠程管理所需的用戶名和密碼。請注意,對于Cube來說,每次只能通過一個客戶端進行管理。 因此,當本地管理軟件運行時,移動應用程序或遠程管理將無法工作。 只要在端口62910上有一個打開的TCP會話,那么其他客戶端就無法與此端口通信了。所以,本地網絡中可以到達Cube的62910端口的每個客戶端,都可以通過連接到Cube的這個端口來阻止其他客戶端登陸。
Cube和遠程管理工具之間的通信是加密形式的,但不是SSL / TLS ...它們的通信是借助HTTP POST請求來完成的,但是只對POST主體進行了加密。HTTP的頭部如下所示:
- POST /cube HTTP/1.1
- Host: smarthome.md.de
- connection: close
- Content-Length: 32
- Opt: "http://www.eq-3.com/MAX", ns=MAX
- MAX-Serial: KMD1016788
這些請求將發送到http://smarthome.md.de:8080。其中,一個相當重要的頭部是MAX-Serial。它必須包括有效的序列號,否則服務器將只是返回500 Internal Server Errors。
AES密碼
AES密碼用于加密發送到遠程管理工具的POST主體。Cube能夠支持"e"消息和"d"消息,這兩類消息分別實現了“加密”和“解密”功能。這樣的話,我們不僅可以對任意字符串進行加密,還能對Cube加密的任何字符串進行解密。下面有一個簡單的例子:
- ~ » ncat 192.168.0.222 62910
- [...]
- e:TEST^M
- E:kvJcZ8bVAoyXaE7gK+q2Ug==
- d:kvJcZ8bVAoyXaE7gK+q2Ug==^M
- D:TESTAAAAAAAAAAAAAAAAAA==
注意:Cube要求命令必須以“\ r \ n”結尾,否則它不會給予響應。為此,在netcat / ncat這樣的工具中發送命令時,不要直接按下RETURN,而應該先按CTRL + v,然后再按RETURN(由行末尾的“^ M”表示)。
該示例展示了Cube是如何對字符串TEST進行加密的,返回的密文是以Base64編碼的字符串kvJcZ8bVAoyXaE7gK + q2Ug ==。 為了對這個字符串進行解密,您可以使用它的解密功能,這樣就可以得到明文字符串TEST了。字符串的其余部分只是經過編碼的空字節(0x00),用于填充密文,使其符合AES塊大小的要求。
加密的字符串通常使用Base64編碼。下面,讓我們看一個真實的例子:為了使用遠程登錄,我們需要設置用戶名和密碼。從Cube發送到遠程系統的明文請求如下所示:
- H:KMD1016788,099c3e,0113
- B:FOOBAR1,5a8a4a5d3c1bd612b8bf1e2fecf609f7,1,SuperSecret
第一行包括的內容是序列號、RF地址和固件版本。 第二行包括用戶名(FOOBAR1)、MD5哈希值,數字(1)和實際密碼(SuperSecret)。MD5哈希值是 password||serial_number的哈希值:
- ~ » echo -n "SuperSecretKMD1016788"|openssl md5
- (stdin)= 5a8a4a5d3c1bd612b8bf1e2fecf609f7
為了將這個有效載荷發送到遠程系統,我們必須進行Base64編碼:
- ~ » echo -n "H:KMD1016788,099c3e,0113
- B:FOOBAR1,5a8a4a5d3c1bd612b8bf1e2fecf609f7,1,SuperSecret"|base64 -w0
- SDpLTUQxMDE2Nzg4LDA5OWMzZSwwMTEzCkI6Rk9PQkFSMSw1YThhNGE1ZDNjMWJkNjEyYjhiZjFlMmZlY2Y2MDlmNywxLFN1cGVyU2VjcmV0
然后進行加密:
- e:SDpLTUQxMDE2Nzg4LDA5OWMzZSwwMTEzCkI6Rk9PQkFSMSw1YThhNGE1ZDNjMWJkNjEyYjhiZjFlMmZlY2Y2MDlmNywxLFN1cGVyU2VjcmV0^M
- E:kGxTXPZVm8CQGcurInyvX3z4C+6zKKKcuS8Wp259XC1yKUfN8tFIfRt0s3qRliIcUGSAcuhuDzl7fpT6fWOnyysSxk9TG1cXtrcVkeNWUzgeO5poXjS5tJlXWgV64ibG
我們現在可以將該Base64字符串復制到Burpsuite的中繼器中,進行Base64解碼(選中它,然后按CTRL + Shift + b),并將其發送到服務器,具體如下圖所示:
如圖所示,服務器返回了一個200 OK響應,說明我們的請求成功了。之后,我們就可以使用用戶名FOOBAR1和密碼SuperSecret登錄到http://smarthome.md.de/的Web界面了。 為了給響應消息進行解密,我們可以對響應的主體進行Base64編碼(只選中響應的主體,并按CTRL + b),然后將其發送到Cube,利用其解密功能進行處理:
- d:QAINuzPCglmG1nNNI/ylrbV6AXKdtBQbkNXT/pMobpXSeuP6/tZtCIq8GD5YSHjK^M
- D:aTowMDAwNTFiMSwwMDAwMDAwMCxmZmZmZmZmZg0KYjpPSw0KAAAAAAAAAAAAAAAA
然后,對得到的Base64字符串進行解碼:
- ~ » echo -n "aTowMDAwNTFiMSwwMDAwMDAwMCxmZmZmZmZmZg0KYjpPSw0KAAAAAAAAAAAAAAAA"|base64 -d
- i:000051b1,00000000,ffffffff
- b:OK
這里的重要問題是:該設備是如何加密該字符串的,加密密鑰是什么? 當談論AES加密時,你必須弄清楚:
使用的密鑰大小是多少? AES支持128、192和256位密鑰。
使用什么操作模式?
根據操作模式:初始化向量(IV)是什么?
第一個問題很容易回答:在供應商頁面上,他們說它使用的是AES-128。 那么操作模式是什么呢? 知道了它,我們就可以加密任意字符串。 最基本的操作模式是ECB:每個16字節塊都被獨立加密,對于它來說,如果對明文加密兩次后會得到相同的密文。 我已經通過字符串( 16 * “\xff”的Base64編碼)進行了測試,這個字符串的大小正好等于AES密碼的塊大小:
- e://///////////////////w==^M
- E:XQfNd8PcLZgnJbwGTuTx5A==
- e://///////////////////w==^M
- E:XQfNd8PcLZgnJbwGTuTx5A==
我們可以看到,對相同的明文(///////////////////// w ==)加密兩次,得到的密文是一致的,那么這可能意味著使用的是ECB 。 但是,讓我們看看多個塊是否是單獨進行加密的。 以下示例將會加密32 *“\ xff”(兩個塊):
- e://///////////////////w==^M
- E:XQfNd8PcLZgnJbwGTuTx5A==
- e://////////////////////////////////////////8=^M
- E:XQfNd8PcLZgnJbwGTuTx5LM36fXWGGUjgVLWxtzwCgo=
如果它使用了ECB模式,那么密文應包含兩份先前看到的字節序列:
- cryptotest » echo -n "XQfNd8PcLZgnJbwGTuTx5A=="|base64 -d |xxd
- 00000000: 5d07 cd77 c3dc 2d98 2725 bc06 4ee4 f1e4 ]..w..-.'%..N...
- cryptotest » echo -n "XQfNd8PcLZgnJbwGTuTx5LM36fXWGGUjgVLWxtzwCgo="|base64 -d |xxd
- 00000000: 5d07 cd77 c3dc 2d98 2725 bc06 4ee4 f1e4 ]..w..-.'%..N...
- 00000010: b337 e9f5 d618 6523 8152 d6c6 dcf0 0a0a .7....e#.R......
我們可以在hexdump中看到,第一個16字節的確與以前的加密結果一致,但是第二個塊是完全不同的。 實際上,ECB加密結果應該是這樣的:
- cryptotest » xxd plain_16
- 00000000: ffff ffff ffff ffff ffff ffff ffff ffff ................
- cryptotest » xxd plain_32
- 00000000: ffff ffff ffff ffff ffff ffff ffff ffff ................
- 00000010: ffff ffff ffff ffff ffff ffff ffff ffff ................
- cryptotest » openssl enc -aes-128-ecb -in plain_16 -nosalt -nopad -k TEST |xxd
- 00000000: cb30 66d5 3db8 89f6 da4b 5831 d29c 6b9f .0f.=....KX1..k.
- cryptotest » openssl enc -aes-128-ecb -in plain_32 -nosalt -nopad -k TEST |xxd
- 00000000: cb30 66d5 3db8 89f6 da4b 5831 d29c 6b9f .0f.=....KX1..k.
- 00000010: cb30 66d5 3db8 89f6 da4b 5831 d29c 6b9f .0f.=....KX1..k.
我們現在知道,它不是ECB。第二種猜測是使用CBC模式進行的加密。CBC在進行AES加密之前,先對第一個塊用初始向量IV進行XOR運算,而所有后續塊將與前一塊的密文進行XOR運算。這樣做的好處是防止相同的明文加密兩次,會產生兩份相同的密文。對于這個IV來說,就是每次加密時生成的一個隨機數字。所以這里合理的猜測是,這個Cube使用的是靜態IV的CBC。
但是,要想解密密文的話,我們首先需要獲得相應的加密密鑰。我這里的猜測是,密鑰可能是基于序列號的,因為如果MAX-serial頭部中包含的是另一個序列號(即使有效)的話,那么遠程服務器就不會接受密文。然而,密鑰從未露面,同時Cube和遠程服務器在磋商加密參數的時候也沒有進行握手。所以,我猜測這個密鑰可能是通過序列號計算得到的。
除了軟件方面之外,我還還考察了Cube的硬件。結果是,電路板本身是很小,正面除了序列號之外,好像也沒有其他有用的信息。但是,我把它翻過來的時候,有趣的東西出現了……
背面有幾個QR碼,包括MAC地址,RF地址,序列號,以及一個...KEY ...?
標識為KEY的QR碼包含以“k”(可能是"key"的意思)為前綴的MD5哈希值,所以,我們不妨嘗試用這個密鑰來解密密文:
- ~ » echo -n "XQfNd8PcLZgnJbwGTuTx5LM36fXWGGUjgVLWxtzwCgo="|base64 -d |openssl enc -aes-128-cbc -d -nopad -nosalt -K 98bbce3f1b25df8e6894b779456d330e -iv 00 |xxd
- 00000000: c975 1589 ed36 536c c975 1589 ed36 536c .u...6Sl.u...6Sl
- 00000010: ffff ffff ffff ffff ffff ffff ffff ffff ................
棒極了!您可以看到,第二個塊已正確解密了。 實際上,第一個塊應該是相同的,但它看起來卻是完全隨機的。 在上一個命令中,我使用了空字節來作為IV(-iv 00)。但是,不要忘了,在解密CBC模式中的最后一個塊之后,需要將最后一個塊與IV進行XOR。這樣就好理解了:由于第一個塊(在解密期間將被最后處理)與一個錯誤的值進行了異或運算,所以才導致了不同的明文。
然而,在這種情況下獲得IV是相當容易的,因為我們知道明文。我們只要將前面得到的第一個塊與明文(即16 *“\ xff”)進行異或運算,就能得到正確的IV了。現在,請打開一個Python shell,只需要進行如下所示的操作即可:
- >>> hex(0xc9751589ed36536cc9751589ed36536c^0xffffffffffffffffffffffffffffffff)
- '0x368aea7612c9ac93368aea7612c9ac93L'
現在,讓我們用這里的IV再次對密文進行解密:
- ~ » echo -n "XQfNd8PcLZgnJbwGTuTx5LM36fXWGGUjgVLWxtzwCgo="|base64 -d |openssl enc -aes-128-cbc -d -nopad -nosalt -K 98bbce3f1b25df8e6894b779456d330e -iv 368aea7612c9ac93368aea7612c9ac93 |xxd
- 00000000: ffff ffff ffff ffff ffff ffff ffff ffff ................
- 00000010: ffff ffff ffff ffff ffff ffff ffff ffff ................
如你所見,我們的明文已經完全恢復了!我們還可以解密遠程服務器的響應:
- ~ » echo -n "QAINuzPCglmG1nNNI/ylrbV6AXKdtBQbkNXT/pMobpXSeuP6/tZtCIq8GD5YSHjK"|base64 -d |openssl enc -aes-128-cbc -d -nopad -nosalt -K 98bbce3f1b25df8e6894b779456d330e -iv 368aea7612c9ac93368aea7612c9ac93
- i:000051b1,00000000,ffffffff
- b:OK
現在,我們已經掌握了在Cube和遠程服務器上對字符串進行加密和解密的所有秘密。同時,我還實現了一個小的Python腳本,不僅使得加密/解密字符串變得更加簡單,同時還能完成適當的填充操作。
網絡發現的自動化
在我看來,分析未知設備的一個重要部分,就是讓其他人也能使用已獲得的信息,以支持他人的進一步研究,或能夠讓人們用通用工具來發現這樣的設備。當涉及到發現網絡上的設備時,我選擇的通用工具是Nmap。除了純端口掃描之外,它還提供了大量已知服務的簽名,同時,我們還可以通過NSE腳本來對其功能進行擴展。
NSE腳本是用Lua語言編寫的,而Lua又是一種相當簡單和易于理解的腳本語言。 開始編寫自己的腳本時,最簡單的方法是就是學習現有的腳本(腳本通常位于/usr/share/nmap/scripts目錄中,或者在線查找)。例如,對于身份請求來說,我們只需要發送一個UDP包,然后檢索響應的有效載荷即可。一個淺顯易懂例子是daytime.nse腳本,具體如下所示:
- portrule = shortport.port_or_service(13, "daytime", {"tcp", "udp"})
- action = function(host, port)
- local status, result = comm.exchange(host, port, "dummy", {lines=1})
- if status then
- return result
- end
- end
在開頭部分,只是定義了一些元數據,實際上對于每個NSE腳本來說,真正的起始位置都是從portrule這里開始的。它定義了該腳本的運行時機。就本例來說,如果13端口已經打開了,并且與端口13或TCP或UDP服務匹配的時候,就會運行該腳本。 NSE腳本中的第二個重要的事情是action,它可以被看作是NSE腳本的main()函數。action總是需要兩個參數:主機和端口。應當指出,這些不僅僅是一個包含主機名或IP地址和端口號的字符串,每個都是一個保存了諸如主機表(host.mac_addr)中的MAC地址或端口表(port.protocol)中的協議(TCP或UDP)之類附加信息的表。
這個腳本使用了comm模塊中的exchange()函數,而該模塊是Nmap提供的諸多LUA模塊之一。這個函數的作用,只是發送一個有效載荷并返回響應。如果腳本需要向用戶返回信息的話,可以通過純字符串或LUA表的形式來返回。
作為Nmap腳本的第一個例子,這里只是在TCP端口62910連接Cube設備,并解析該設備返回的第一行內容,從而輸出該Cube設備的序列號、RF地址和固件版本 。
- H:KMD1016788,099c3e,0113,00000000,7ee2b5d7,00,32,100408,002c,03,0000
- [...]
所以,我們的腳本只需要連接到該端口,獲得響應并解析值KMD1016788(序列號)、099c3e(RF地址)和0113(固件版本),代碼具體如下所示:
- local shortport = require "shortport"
- local stdnse = require "stdnse"
- description = [[
- ]]
- author = "CHANGEME"
- license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
- categories = {"discovery", "safe"}
- portrule = shortport.portnumber(0, "tcp")
- action = function(host, port)
- end
在上面的代碼的基礎之上,可以繼續添加所需的功能。 因為我們只需要連接到一個端口來獲取響應而不發送任何東西,所以最簡單的方法是使用一個簡單的套接字。有了Nmap后,利用NSE腳本進行socket通信變得異常輕松:
- local sock = nmap.new_socket()
- local status, err = sock:connect(host, port, "tcp")
- if not status then
- stdnse.debug1("%s", err)
- return
- end
- local status, data = sock:receive()
- if not status or not data then
- stdnse.debug1("%s", "Could not receive any data")
- return
- end
這樣就可以在變量ret中接收響應了,然后解析該變量,就能提取所需的信息了:
- local output = stdnse.output_table()
- local serial, rf_address, firmware
- for serial,rf_address,firmware in data:gmatch("H:(%u%u%u%d%d%d%d%d%d%d),(%x%x%x%x%x%x),(%d%d%d%d),") do
- output["MAX Serial:"] = serial
- output["RF Address"] = rf_address
- output["Firmware Version"] = firmware
- end
現在,我們就有了一個輸出表,其中包含了需要返回給用戶的所有信息。正如前面說過的一樣,為此只需在action的末尾放上一個“return output”即可。 完整的腳本可以在這里下載。
我們可以測試該腳本,檢查是否能夠正常工作:
- max-cube/nse » nmap --script maxcube-info.nse -Pn -p 62910 192.168.0.222
- Starting Nmap 7.12SVN ( https://nmap.org ) at 2016-04-08 01:11 CEST
- Nmap scan report for 192.168.0.222
- Host is up (0.0051s latency).
- PORT STATE SERVICE
- 62910/tcp open unknown
- | maxcube-info:
- | MAX Serial:: KMD1016788
- | RF Address: 099c3e
- |_ Firmware Version: 0113
從上面的結果來看,我們的腳本工作正常。但它可能是非常不可靠的:就像我前面提到的,當端口62910上有一個開放的TCP連接(例如管理軟件正在運行或有人通過netcat連接該端口)的時候,Nmap將無法與該端口進行通信,那么這個腳本自然就無法正常工作了。
一些有用的提示:
stdnse模塊提供了debug()函數,可以用來在腳本中打印所有的調試輸出。為此,至少需要提供一個-d命令行參數。
命令行參數-script-trace能夠提供調試NSE腳本所需的更詳細的輸出結果。
為了在已標識為打開的所有端口上運行該腳本,請在腳本名稱前面加上前綴+,例如不要用“-script myscript.nse”,而是使用“-script + myscript.nse”
接下來要做什么?
還有兩個大問題需要解決:
加密密鑰來自哪里?
我認為加密密鑰(看起來像一個MD5哈希值)是只有供應商知道的密碼串與序列號的哈希值。由于供應商可以區分密文,這意味著每個設備都可能有一個自己的密鑰(我只有一個設備可以測試)。
一個不同的論點:我在電路板上的QR碼中發現的密鑰。這可能表示,密鑰在設備的制造期間就已經確定下來了。 加密密鑰可以是完全隨機的,并且甚至可能不是包括序列號的任何明文的散列值。但這意味著供應商將需要建立一個列表,以便在制造期間將所有的序列號都映射為相應的密鑰。
文件firmware.enc是如何加密的?
Cube提供了更新功能,也就是可以通過UDP數據包發送一些新版本的固件(記住,這是未經驗證的)。 然而,固件文件是不可讀的,并且需要在該設備上進行解密,因為管理軟件只能解析文件,但無法解密它們。我已經編寫了一個簡單的解析代碼(地址https://github.com/ernw/insinuator-snippets/tree/master/maxcube/firmware/parser),但固件本身似乎是加密的。
不過,我猜修改固件是一件很酷的事情。
編寫其他Nmap腳本
正如在文章開頭所看到的那樣,至少有兩種方法可以識別網絡上的Cube:多播或單播UDP數據包。然而,這些方法還是需要一點技巧的,因為響應的源端口是靜態的(總是23272)。所以,如果你打算在腳本中使用Nmap的comm.exchange()函數的話,那么是無法在函數中獲得任何響應的,因為它會使用隨機的源端口。
這個問題的解決方案是發送組播數據包,然后使用pcap捕獲響應。實際上,Nmap的一些腳本已經可以做到這一點了,例如我就寫過一個腳本,專門利用類似的技術來尋找KNX設備。
結束語
根據我們分析IoT設備的經驗,大部分物聯網設備都存在許多常見的安全漏洞,如缺乏身份驗證或驗證不足等。 此外,在網絡上識別這樣的設備通常是輕而易舉的事情,因為它們具有一些“奇異”的屬性,例如響應分組中的固定源端口或者僅允許單個TCP連接等。所以,我希望分析未知設備的研究人員也開始共享他們的研究成果,例如向Nmap等項目貢獻代碼,以便幫助更多的人。