物聯(lián)網(wǎng)設(shè)備安全分析之MAX! Cube LAN Gateway篇
前言
在這篇文章中,我們將為讀者介紹如何對(duì)物聯(lián)網(wǎng)設(shè)備進(jìn)行安全評(píng)估。這里將會(huì)詳細(xì)介紹進(jìn)行評(píng)估所需的基本方法:對(duì)于不同的任務(wù)需要使用哪些工具,以及如何解決在分析過(guò)程中可能出現(xiàn)的問(wèn)題。本文的目標(biāo)讀者對(duì)為對(duì)物聯(lián)網(wǎng)設(shè)備安全分析感興趣的朋友,對(duì)逆向工程感興趣的讀者,或者只想了解如何通過(guò)技術(shù)手段來(lái)處理未知設(shè)備的讀者。
本文的重點(diǎn)不在于揭示某種設(shè)備的某種漏洞,而在于闡釋影響各種IoT設(shè)備的安全弱點(diǎn),因此,本文介紹的內(nèi)容同樣適用于其他的設(shè)備和場(chǎng)景。
分析對(duì)象
本文的分析對(duì)象是來(lái)自eQ-3公司的 MAX! Cube LAN Gateway (以下稱為“Cube”)。實(shí)際上,許多產(chǎn)品都捆綁了該設(shè)備,比如我的加熱控制系統(tǒng)中就帶有該設(shè)備。通過(guò)該設(shè)備名稱中的“Cube”不難猜出,它只是一個(gè)LAN網(wǎng)關(guān),通過(guò)RF技術(shù)實(shí)現(xiàn)真正的“物聯(lián)網(wǎng)設(shè)備”或“智能設(shè)備”之間的通信。在本文中,我們將重點(diǎn)介紹以太網(wǎng)通信,因?yàn)樗枪芾碥浖闹饕ㄐ欧绞健?/p>
搭建中間人攻擊場(chǎng)景
為了全面地了解該設(shè)備的通信狀況,我搭建了一個(gè)簡(jiǎn)單的中間人攻擊場(chǎng)景。我在自己的系統(tǒng)上使用了一個(gè)USB網(wǎng)卡,并將其直接連接到Cube。首先,打開Cube,但是不要使用任何管理客戶端或其他需要通信的軟件,這樣就能了解Cube自身發(fā)送了什么數(shù)據(jù)包。我們發(fā)現(xiàn),它只是試圖通過(guò)DHCP獲得IP,然后開始解析ntp.homematic.com。
為了讓Cube可以訪問(wèn)互聯(lián)網(wǎng),我已將自己的USB網(wǎng)卡配置為Cube的路由器。為了在不使用DHCP的時(shí)候可以通過(guò)192.168.0.222訪問(wèn)Cube,我把設(shè)備的IP地址設(shè)為192.168.0.1/24,并進(jìn)行了如下所示的配置,以允許通過(guò)USB網(wǎng)卡的NAT訪問(wèn)互聯(lián)網(wǎng):
- 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
注意:設(shè)備enp0s20u9u3是連接到Cube的USB網(wǎng)卡,enp0s25是系統(tǒng)上的另一個(gè)網(wǎng)卡,該網(wǎng)卡連接到具有互聯(lián)網(wǎng)連接的路由器上。
因?yàn)镃ube會(huì)向默認(rèn)的路由器發(fā)送DNS查詢,所以,我們可以在機(jī)器上設(shè)置一個(gè)DNS服務(wù)器,或者直接將DNS查詢轉(zhuǎn)發(fā)給相應(yīng)的域名服務(wù)器(例如OpenDNS服務(wù)器):
- 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
這樣一來(lái),我們就能設(shè)法觀察Cube發(fā)送的所有的數(shù)據(jù)了,例如使用Wireshark或tcpdump等工具來(lái)嗅探連接到Cube的NIC上通信數(shù)據(jù)。
發(fā)現(xiàn)網(wǎng)絡(luò)中的Cube設(shè)備
為了方便管理員自動(dòng)識(shí)別本地網(wǎng)絡(luò)上的Cube設(shè)備,Cube提供了相應(yīng)的網(wǎng)絡(luò)發(fā)現(xiàn)功能。下面的Wireshark屏幕截圖展示了由本地管理軟件發(fā)送的數(shù)據(jù)包:
該軟件會(huì)向23272端口上的多播組地址224.0.01發(fā)送UDP數(shù)據(jù)包。在右下方,標(biāo)記出來(lái)的hexdump部分是有效載荷(eQ3Max * \ x00 ********** I)。這是一個(gè)所謂的身份消息,用來(lái)命令Cube向數(shù)據(jù)包的源主機(jī)報(bào)告其序列號(hào)。下面的截圖顯示了Cube的響應(yīng):
同樣,這里響應(yīng)的有效載荷也在右下側(cè)(eQ3MaxApKMD1016788> I)做了標(biāo)記。 我的設(shè)備的序列號(hào)是KMD1016788,所以一切正?!,F(xiàn)在,我們只要發(fā)送這樣的UDP數(shù)據(jù)包,就能輕松找出本地網(wǎng)絡(luò)上所有的Cube設(shè)備。同時(shí),我們也可以檢查某個(gè)主機(jī)是否是使用單播數(shù)據(jù)包的Cube設(shè)備。
我們自己也可以發(fā)送所有這些數(shù)據(jù)包,甚至單播數(shù)據(jù)包。對(duì)于這些任務(wù),我更喜歡使用Scapy來(lái)完成。因?yàn)橛辛怂覀兙涂梢栽诮换ナ絇ython shell中創(chuàng)建、發(fā)送/接收和操作數(shù)據(jù)包,這樣做是很方便的,因?yàn)檫@樣可以同時(shí)進(jìn)行其他任務(wù),如進(jìn)行計(jì)算或數(shù)據(jù)轉(zhuǎn)換。Cube使用的UDP端口是23272。我們可以從前面的示例中獲取相應(yīng)字符串(這是一個(gè)“身份消息(identity message)”,由有效載荷末尾的“I”表示),并將其發(fā)送到目標(biāo)主機(jī):
- >>> 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")
響應(yīng)的有效載荷的內(nèi)容如下所示:
- 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發(fā)送的響應(yīng)數(shù)據(jù)包的源和目標(biāo)端口總是23272。所以,你要么一直使用23272作為源端口來(lái)獲得響應(yīng),要么使用pcap進(jìn)行帶外捕獲——如果你使用隨機(jī)源端口的話。
實(shí)際上身份消息是非常有用的:所有其他UDP消息都需要該Cube的序列號(hào),它將被放入請(qǐng)求的響應(yīng)中。有了這個(gè)序列號(hào),我們就可以發(fā)送其他消息類型了,例如 “重新啟動(dòng)消息(reboot message)”(在有效載荷末尾用“R”進(jìn)行標(biāo)識(shí)):
- >>> 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")
就像該消息的名稱所示,它會(huì)重新啟動(dòng)該設(shè)備。這種消息可以從本地網(wǎng)絡(luò)上的任何設(shè)備發(fā)出,無(wú)需任何身份驗(yàn)證。
如何管理Cube
Cube的管理方式有三種:
本地管理軟件:這是一個(gè)可安裝在Windows上的EXE程序,它會(huì)在一個(gè)較大的隨機(jī)端口上啟動(dòng)一個(gè)本地Web服務(wù)器,用于java applet…
遠(yuǎn)程管理軟件:功能與登錄界面基本相同,只不過(guò)是托管在云中而已。
移動(dòng)應(yīng)用:我還沒(méi)有見過(guò)。
本地和遠(yuǎn)程管理軟件的主要區(qū)別是遠(yuǎn)程軟件的通信是加密的,這一點(diǎn)將在下文詳細(xì)介紹。
本地管理軟件非常有助于深入了解Cube的內(nèi)部工作原理,因?yàn)橥ㄟ^(guò)觸發(fā)不同的功能,我們就可以觀察發(fā)送給Cube的相應(yīng)請(qǐng)求了。為此,我們可以每次執(zhí)行一個(gè)功能,然后捕獲相應(yīng)的流量,從而大概了解哪些事情是可以通過(guò)Cube的遠(yuǎn)程管理來(lái)執(zhí)行的。
與“遠(yuǎn)程代理”進(jìn)行交互
為了使用遠(yuǎn)程管理軟件,您必須在本地管理軟件中配置遠(yuǎn)程管理所需的用戶名和密碼。請(qǐng)注意,對(duì)于Cube來(lái)說(shuō),每次只能通過(guò)一個(gè)客戶端進(jìn)行管理。 因此,當(dāng)本地管理軟件運(yùn)行時(shí),移動(dòng)應(yīng)用程序或遠(yuǎn)程管理將無(wú)法工作。 只要在端口62910上有一個(gè)打開的TCP會(huì)話,那么其他客戶端就無(wú)法與此端口通信了。所以,本地網(wǎng)絡(luò)中可以到達(dá)Cube的62910端口的每個(gè)客戶端,都可以通過(guò)連接到Cube的這個(gè)端口來(lái)阻止其他客戶端登陸。
Cube和遠(yuǎn)程管理工具之間的通信是加密形式的,但不是SSL / TLS ...它們的通信是借助HTTP POST請(qǐng)求來(lái)完成的,但是只對(duì)POST主體進(jìn)行了加密。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
這些請(qǐng)求將發(fā)送到http://smarthome.md.de:8080。其中,一個(gè)相當(dāng)重要的頭部是MAX-Serial。它必須包括有效的序列號(hào),否則服務(wù)器將只是返回500 Internal Server Errors。
AES密碼
AES密碼用于加密發(fā)送到遠(yuǎn)程管理工具的POST主體。Cube能夠支持"e"消息和"d"消息,這兩類消息分別實(shí)現(xiàn)了“加密”和“解密”功能。這樣的話,我們不僅可以對(duì)任意字符串進(jìn)行加密,還能對(duì)Cube加密的任何字符串進(jìn)行解密。下面有一個(gè)簡(jiǎn)單的例子:
- ~ » ncat 192.168.0.222 62910
- [...]
- e:TEST^M
- E:kvJcZ8bVAoyXaE7gK+q2Ug==
- d:kvJcZ8bVAoyXaE7gK+q2Ug==^M
- D:TESTAAAAAAAAAAAAAAAAAA==
注意:Cube要求命令必須以“\ r \ n”結(jié)尾,否則它不會(huì)給予響應(yīng)。為此,在netcat / ncat這樣的工具中發(fā)送命令時(shí),不要直接按下RETURN,而應(yīng)該先按CTRL + v,然后再按RETURN(由行末尾的“^ M”表示)。
該示例展示了Cube是如何對(duì)字符串TEST進(jìn)行加密的,返回的密文是以Base64編碼的字符串kvJcZ8bVAoyXaE7gK + q2Ug ==。 為了對(duì)這個(gè)字符串進(jìn)行解密,您可以使用它的解密功能,這樣就可以得到明文字符串TEST了。字符串的其余部分只是經(jīng)過(guò)編碼的空字節(jié)(0x00),用于填充密文,使其符合AES塊大小的要求。
加密的字符串通常使用Base64編碼。下面,讓我們看一個(gè)真實(shí)的例子:為了使用遠(yuǎn)程登錄,我們需要設(shè)置用戶名和密碼。從Cube發(fā)送到遠(yuǎn)程系統(tǒng)的明文請(qǐng)求如下所示:
- H:KMD1016788,099c3e,0113
- B:FOOBAR1,5a8a4a5d3c1bd612b8bf1e2fecf609f7,1,SuperSecret
第一行包括的內(nèi)容是序列號(hào)、RF地址和固件版本。 第二行包括用戶名(FOOBAR1)、MD5哈希值,數(shù)字(1)和實(shí)際密碼(SuperSecret)。MD5哈希值是 password||serial_number的哈希值:
- ~ » echo -n "SuperSecretKMD1016788"|openssl md5
- (stdin)= 5a8a4a5d3c1bd612b8bf1e2fecf609f7
為了將這個(gè)有效載荷發(fā)送到遠(yuǎn)程系統(tǒng),我們必須進(jìn)行Base64編碼:
- ~ » echo -n "H:KMD1016788,099c3e,0113
- B:FOOBAR1,5a8a4a5d3c1bd612b8bf1e2fecf609f7,1,SuperSecret"|base64 -w0
- SDpLTUQxMDE2Nzg4LDA5OWMzZSwwMTEzCkI6Rk9PQkFSMSw1YThhNGE1ZDNjMWJkNjEyYjhiZjFlMmZlY2Y2MDlmNywxLFN1cGVyU2VjcmV0
然后進(jìn)行加密:
- e:SDpLTUQxMDE2Nzg4LDA5OWMzZSwwMTEzCkI6Rk9PQkFSMSw1YThhNGE1ZDNjMWJkNjEyYjhiZjFlMmZlY2Y2MDlmNywxLFN1cGVyU2VjcmV0^M
- E:kGxTXPZVm8CQGcurInyvX3z4C+6zKKKcuS8Wp259XC1yKUfN8tFIfRt0s3qRliIcUGSAcuhuDzl7fpT6fWOnyysSxk9TG1cXtrcVkeNWUzgeO5poXjS5tJlXWgV64ibG
我們現(xiàn)在可以將該Base64字符串復(fù)制到Burpsuite的中繼器中,進(jìn)行Base64解碼(選中它,然后按CTRL + Shift + b),并將其發(fā)送到服務(wù)器,具體如下圖所示:
如圖所示,服務(wù)器返回了一個(gè)200 OK響應(yīng),說(shuō)明我們的請(qǐng)求成功了。之后,我們就可以使用用戶名FOOBAR1和密碼SuperSecret登錄到http://smarthome.md.de/的Web界面了。 為了給響應(yīng)消息進(jìn)行解密,我們可以對(duì)響應(yīng)的主體進(jìn)行Base64編碼(只選中響應(yīng)的主體,并按CTRL + b),然后將其發(fā)送到Cube,利用其解密功能進(jìn)行處理:
- d:QAINuzPCglmG1nNNI/ylrbV6AXKdtBQbkNXT/pMobpXSeuP6/tZtCIq8GD5YSHjK^M
- D:aTowMDAwNTFiMSwwMDAwMDAwMCxmZmZmZmZmZg0KYjpPSw0KAAAAAAAAAAAAAAAA
然后,對(duì)得到的Base64字符串進(jìn)行解碼:
- ~ » echo -n "aTowMDAwNTFiMSwwMDAwMDAwMCxmZmZmZmZmZg0KYjpPSw0KAAAAAAAAAAAAAAAA"|base64 -d
- i:000051b1,00000000,ffffffff
- b:OK
這里的重要問(wèn)題是:該設(shè)備是如何加密該字符串的,加密密鑰是什么? 當(dāng)談?wù)揂ES加密時(shí),你必須弄清楚:
使用的密鑰大小是多少? AES支持128、192和256位密鑰。
使用什么操作模式?
根據(jù)操作模式:初始化向量(IV)是什么?
第一個(gè)問(wèn)題很容易回答:在供應(yīng)商頁(yè)面上,他們說(shuō)它使用的是AES-128。 那么操作模式是什么呢? 知道了它,我們就可以加密任意字符串。 最基本的操作模式是ECB:每個(gè)16字節(jié)塊都被獨(dú)立加密,對(duì)于它來(lái)說(shuō),如果對(duì)明文加密兩次后會(huì)得到相同的密文。 我已經(jīng)通過(guò)字符串( 16 * “\xff”的Base64編碼)進(jìn)行了測(cè)試,這個(gè)字符串的大小正好等于AES密碼的塊大?。?/p>
- e://///////////////////w==^M
- E:XQfNd8PcLZgnJbwGTuTx5A==
- e://///////////////////w==^M
- E:XQfNd8PcLZgnJbwGTuTx5A==
我們可以看到,對(duì)相同的明文(///////////////////// w ==)加密兩次,得到的密文是一致的,那么這可能意味著使用的是ECB 。 但是,讓我們看看多個(gè)塊是否是單獨(dú)進(jìn)行加密的。 以下示例將會(huì)加密32 *“\ xff”(兩個(gè)塊):
- e://///////////////////w==^M
- E:XQfNd8PcLZgnJbwGTuTx5A==
- e://////////////////////////////////////////8=^M
- E:XQfNd8PcLZgnJbwGTuTx5LM36fXWGGUjgVLWxtzwCgo=
如果它使用了ECB模式,那么密文應(yīng)包含兩份先前看到的字節(jié)序列:
- 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中看到,第一個(gè)16字節(jié)的確與以前的加密結(jié)果一致,但是第二個(gè)塊是完全不同的。 實(shí)際上,ECB加密結(jié)果應(yīng)該是這樣的:
- 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.
我們現(xiàn)在知道,它不是ECB。第二種猜測(cè)是使用CBC模式進(jìn)行的加密。CBC在進(jìn)行AES加密之前,先對(duì)第一個(gè)塊用初始向量IV進(jìn)行XOR運(yùn)算,而所有后續(xù)塊將與前一塊的密文進(jìn)行XOR運(yùn)算。這樣做的好處是防止相同的明文加密兩次,會(huì)產(chǎn)生兩份相同的密文。對(duì)于這個(gè)IV來(lái)說(shuō),就是每次加密時(shí)生成的一個(gè)隨機(jī)數(shù)字。所以這里合理的猜測(cè)是,這個(gè)Cube使用的是靜態(tài)IV的CBC。
但是,要想解密密文的話,我們首先需要獲得相應(yīng)的加密密鑰。我這里的猜測(cè)是,密鑰可能是基于序列號(hào)的,因?yàn)槿绻鸐AX-serial頭部中包含的是另一個(gè)序列號(hào)(即使有效)的話,那么遠(yuǎn)程服務(wù)器就不會(huì)接受密文。然而,密鑰從未露面,同時(shí)Cube和遠(yuǎn)程服務(wù)器在磋商加密參數(shù)的時(shí)候也沒(méi)有進(jìn)行握手。所以,我猜測(cè)這個(gè)密鑰可能是通過(guò)序列號(hào)計(jì)算得到的。
除了軟件方面之外,我還還考察了Cube的硬件。結(jié)果是,電路板本身是很小,正面除了序列號(hào)之外,好像也沒(méi)有其他有用的信息。但是,我把它翻過(guò)來(lái)的時(shí)候,有趣的東西出現(xiàn)了……
背面有幾個(gè)QR碼,包括MAC地址,RF地址,序列號(hào),以及一個(gè)...KEY ...?
標(biāo)識(shí)為KEY的QR碼包含以“k”(可能是"key"的意思)為前綴的MD5哈希值,所以,我們不妨嘗試用這個(gè)密鑰來(lái)解密密文:
- ~ » 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 ................
棒極了!您可以看到,第二個(gè)塊已正確解密了。 實(shí)際上,第一個(gè)塊應(yīng)該是相同的,但它看起來(lái)卻是完全隨機(jī)的。 在上一個(gè)命令中,我使用了空字節(jié)來(lái)作為IV(-iv 00)。但是,不要忘了,在解密CBC模式中的最后一個(gè)塊之后,需要將最后一個(gè)塊與IV進(jìn)行XOR。這樣就好理解了:由于第一個(gè)塊(在解密期間將被最后處理)與一個(gè)錯(cuò)誤的值進(jìn)行了異或運(yùn)算,所以才導(dǎo)致了不同的明文。
然而,在這種情況下獲得IV是相當(dāng)容易的,因?yàn)槲覀冎烂魑摹N覀冎灰獙⑶懊娴玫降牡谝粋€(gè)塊與明文(即16 *“\ xff”)進(jìn)行異或運(yùn)算,就能得到正確的IV了?,F(xiàn)在,請(qǐng)打開一個(gè)Python shell,只需要進(jìn)行如下所示的操作即可:
- >>> hex(0xc9751589ed36536cc9751589ed36536c^0xffffffffffffffffffffffffffffffff)
- '0x368aea7612c9ac93368aea7612c9ac93L'
現(xiàn)在,讓我們用這里的IV再次對(duì)密文進(jìn)行解密:
- ~ » 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 ................
如你所見,我們的明文已經(jīng)完全恢復(fù)了!我們還可以解密遠(yuǎn)程服務(wù)器的響應(yīng):
- ~ » 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
現(xiàn)在,我們已經(jīng)掌握了在Cube和遠(yuǎn)程服務(wù)器上對(duì)字符串進(jìn)行加密和解密的所有秘密。同時(shí),我還實(shí)現(xiàn)了一個(gè)小的Python腳本,不僅使得加密/解密字符串變得更加簡(jiǎn)單,同時(shí)還能完成適當(dāng)?shù)奶畛洳僮鳌?/p>
網(wǎng)絡(luò)發(fā)現(xiàn)的自動(dòng)化
在我看來(lái),分析未知設(shè)備的一個(gè)重要部分,就是讓其他人也能使用已獲得的信息,以支持他人的進(jìn)一步研究,或能夠讓人們用通用工具來(lái)發(fā)現(xiàn)這樣的設(shè)備。當(dāng)涉及到發(fā)現(xiàn)網(wǎng)絡(luò)上的設(shè)備時(shí),我選擇的通用工具是Nmap。除了純端口掃描之外,它還提供了大量已知服務(wù)的簽名,同時(shí),我們還可以通過(guò)NSE腳本來(lái)對(duì)其功能進(jìn)行擴(kuò)展。
NSE腳本是用Lua語(yǔ)言編寫的,而Lua又是一種相當(dāng)簡(jiǎn)單和易于理解的腳本語(yǔ)言。 開始編寫自己的腳本時(shí),最簡(jiǎn)單的方法是就是學(xué)習(xí)現(xiàn)有的腳本(腳本通常位于/usr/share/nmap/scripts目錄中,或者在線查找)。例如,對(duì)于身份請(qǐng)求來(lái)說(shuō),我們只需要發(fā)送一個(gè)UDP包,然后檢索響應(yīng)的有效載荷即可。一個(gè)淺顯易懂例子是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
在開頭部分,只是定義了一些元數(shù)據(jù),實(shí)際上對(duì)于每個(gè)NSE腳本來(lái)說(shuō),真正的起始位置都是從portrule這里開始的。它定義了該腳本的運(yùn)行時(shí)機(jī)。就本例來(lái)說(shuō),如果13端口已經(jīng)打開了,并且與端口13或TCP或UDP服務(wù)匹配的時(shí)候,就會(huì)運(yùn)行該腳本。 NSE腳本中的第二個(gè)重要的事情是action,它可以被看作是NSE腳本的main()函數(shù)。action總是需要兩個(gè)參數(shù):主機(jī)和端口。應(yīng)當(dāng)指出,這些不僅僅是一個(gè)包含主機(jī)名或IP地址和端口號(hào)的字符串,每個(gè)都是一個(gè)保存了諸如主機(jī)表(host.mac_addr)中的MAC地址或端口表(port.protocol)中的協(xié)議(TCP或UDP)之類附加信息的表。
這個(gè)腳本使用了comm模塊中的exchange()函數(shù),而該模塊是Nmap提供的諸多LUA模塊之一。這個(gè)函數(shù)的作用,只是發(fā)送一個(gè)有效載荷并返回響應(yīng)。如果腳本需要向用戶返回信息的話,可以通過(guò)純字符串或LUA表的形式來(lái)返回。
作為Nmap腳本的第一個(gè)例子,這里只是在TCP端口62910連接Cube設(shè)備,并解析該設(shè)備返回的第一行內(nèi)容,從而輸出該Cube設(shè)備的序列號(hào)、RF地址和固件版本 。
- H:KMD1016788,099c3e,0113,00000000,7ee2b5d7,00,32,100408,002c,03,0000
- [...]
所以,我們的腳本只需要連接到該端口,獲得響應(yīng)并解析值KMD1016788(序列號(hào))、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
在上面的代碼的基礎(chǔ)之上,可以繼續(xù)添加所需的功能。 因?yàn)槲覀冎恍枰B接到一個(gè)端口來(lái)獲取響應(yīng)而不發(fā)送任何東西,所以最簡(jiǎn)單的方法是使用一個(gè)簡(jiǎn)單的套接字。有了Nmap后,利用NSE腳本進(jìn)行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中接收響應(yīng)了,然后解析該變量,就能提取所需的信息了:
- 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
現(xiàn)在,我們就有了一個(gè)輸出表,其中包含了需要返回給用戶的所有信息。正如前面說(shuō)過(guò)的一樣,為此只需在action的末尾放上一個(gè)“return output”即可。 完整的腳本可以在這里下載。
我們可以測(cè)試該腳本,檢查是否能夠正常工作:
- 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
從上面的結(jié)果來(lái)看,我們的腳本工作正常。但它可能是非常不可靠的:就像我前面提到的,當(dāng)端口62910上有一個(gè)開放的TCP連接(例如管理軟件正在運(yùn)行或有人通過(guò)netcat連接該端口)的時(shí)候,Nmap將無(wú)法與該端口進(jìn)行通信,那么這個(gè)腳本自然就無(wú)法正常工作了。
一些有用的提示:
stdnse模塊提供了debug()函數(shù),可以用來(lái)在腳本中打印所有的調(diào)試輸出。為此,至少需要提供一個(gè)-d命令行參數(shù)。
命令行參數(shù)-script-trace能夠提供調(diào)試NSE腳本所需的更詳細(xì)的輸出結(jié)果。
為了在已標(biāo)識(shí)為打開的所有端口上運(yùn)行該腳本,請(qǐng)?jiān)谀_本名稱前面加上前綴+,例如不要用“-script myscript.nse”,而是使用“-script + myscript.nse”
接下來(lái)要做什么?
還有兩個(gè)大問(wèn)題需要解決:
加密密鑰來(lái)自哪里?
我認(rèn)為加密密鑰(看起來(lái)像一個(gè)MD5哈希值)是只有供應(yīng)商知道的密碼串與序列號(hào)的哈希值。由于供應(yīng)商可以區(qū)分密文,這意味著每個(gè)設(shè)備都可能有一個(gè)自己的密鑰(我只有一個(gè)設(shè)備可以測(cè)試)。
一個(gè)不同的論點(diǎn):我在電路板上的QR碼中發(fā)現(xiàn)的密鑰。這可能表示,密鑰在設(shè)備的制造期間就已經(jīng)確定下來(lái)了。 加密密鑰可以是完全隨機(jī)的,并且甚至可能不是包括序列號(hào)的任何明文的散列值。但這意味著供應(yīng)商將需要建立一個(gè)列表,以便在制造期間將所有的序列號(hào)都映射為相應(yīng)的密鑰。
文件firmware.enc是如何加密的?
Cube提供了更新功能,也就是可以通過(guò)UDP數(shù)據(jù)包發(fā)送一些新版本的固件(記住,這是未經(jīng)驗(yàn)證的)。 然而,固件文件是不可讀的,并且需要在該設(shè)備上進(jìn)行解密,因?yàn)楣芾碥浖荒芙馕鑫募?,但無(wú)法解密它們。我已經(jīng)編寫了一個(gè)簡(jiǎn)單的解析代碼(地址https://github.com/ernw/insinuator-snippets/tree/master/maxcube/firmware/parser),但固件本身似乎是加密的。
不過(guò),我猜修改固件是一件很酷的事情。
編寫其他Nmap腳本
正如在文章開頭所看到的那樣,至少有兩種方法可以識(shí)別網(wǎng)絡(luò)上的Cube:多播或單播UDP數(shù)據(jù)包。然而,這些方法還是需要一點(diǎn)技巧的,因?yàn)轫憫?yīng)的源端口是靜態(tài)的(總是23272)。所以,如果你打算在腳本中使用Nmap的comm.exchange()函數(shù)的話,那么是無(wú)法在函數(shù)中獲得任何響應(yīng)的,因?yàn)樗鼤?huì)使用隨機(jī)的源端口。
這個(gè)問(wèn)題的解決方案是發(fā)送組播數(shù)據(jù)包,然后使用pcap捕獲響應(yīng)。實(shí)際上,Nmap的一些腳本已經(jīng)可以做到這一點(diǎn)了,例如我就寫過(guò)一個(gè)腳本,專門利用類似的技術(shù)來(lái)尋找KNX設(shè)備。
結(jié)束語(yǔ)
根據(jù)我們分析IoT設(shè)備的經(jīng)驗(yàn),大部分物聯(lián)網(wǎng)設(shè)備都存在許多常見的安全漏洞,如缺乏身份驗(yàn)證或驗(yàn)證不足等。 此外,在網(wǎng)絡(luò)上識(shí)別這樣的設(shè)備通常是輕而易舉的事情,因?yàn)樗鼈兙哂幸恍?ldquo;奇異”的屬性,例如響應(yīng)分組中的固定源端口或者僅允許單個(gè)TCP連接等。所以,我希望分析未知設(shè)備的研究人員也開始共享他們的研究成果,例如向Nmap等項(xiàng)目貢獻(xiàn)代碼,以便幫助更多的人。