物联网设备安全分析之MAX!CubeLANGateway篇

前言

在这篇文章中,我们将为读者介绍如何对物联网设备进行安全评估。这里将会详细介绍进行评估所需的基本方法:对于不同的任务需要使用哪些工具,以及如何解决在分析过程中可能出现的问题。本文的目标读者对为对物联网设备安全分析感兴趣的朋友,对逆向工程感兴趣的读者,或者只想了解如何通过技术手段来处理未知设备的读者。

本文的重点不在于揭示某种设备的某种漏洞,而在于阐释影响各种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访问互联网:

 
 
 
 
  1. sysctl net.ipv4.ip_forward=1 
  2. iptables -t nat -A POSTROUTING -o enp0s25 -j MASQUERADE 
  3. iptables -A FORWARD -i enp0s20u9u3 -o enp0s25 -j ACCEPT 
  4. iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 

注意:设备enp0s20u9u3是连接到Cube的USB网卡,enp0s25是系统上的另一个网卡,该网卡连接到具有互联网连接的路由器上。

因为Cube会向默认的路由器发送DNS查询,所以,我们可以在机器上设置一个DNS服务器,或者直接将DNS查询转发给相应的域名服务器(例如OpenDNS服务器):

 
 
 
 
  1. iptables -t nat -A PREROUTING -i enp0s20u9u3 -p udp --dport 53 -j DNAT --to 208.67.222.222:53 
  2. 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”表示),并将其发送到目标主机:

 
 
 
 
  1. >>> p = Ether()/IP(src="192.168.0.1", dst="192.168.0.222")/UDP(sport=23272, dport=23272)/Raw("eQ3Max*\x00**********I") 
  2. >>> sendp(p, iface="enp0s20u9u3") 

响应的有效载荷的内容如下所示:

 
 
 
 
  1. 00000000  65 51 33 4d 61 78 41 70  4b 4d 44 31 30 31 36 37 eQ3MaxAp KMD10167 
  2. 00000010  38 38 3e 49 00 09 9c 3e  01 13                   88>I...> .. 

注意:Cube发送的响应数据包的源和目标端口总是23272。所以,你要么一直使用23272作为源端口来获得响应,要么使用pcap进行带外捕获——如果你使用随机源端口的话。

实际上身份消息是非常有用的:所有其他UDP消息都需要该Cube的序列号,它将被放入请求的响应中。有了这个序列号,我们就可以发送其他消息类型了,例如 “重新启动消息(reboot message)”(在有效载荷末尾用“R”进行标识):

 
 
 
 
  1. >>> p = Ether()/IP(src="192.168.0.1", dst="192.168.0.10")/UDP(sport=23272, dport=23272)/Raw("eQ3Max*\x00KMD1016788R") 
  2. >>> 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的头部如下所示:

 
 
 
 
  1. POST /cube HTTP/1.1 
  2. Host: smarthome.md.de 
  3. connection: close 
  4. Content-Length: 32 
  5. Opt: "http://www.eq-3.com/MAX", ns=MAX 
  6. MAX-Serial: KMD1016788 

这些请求将发送到http://smarthome.md.de:8080。其中,一个相当重要的头部是MAX-Serial。它必须包括有效的序列号,否则服务器将只是返回500 Internal Server Errors。

AES密码

AES密码用于加密发送到远程管理工具的POST主体。Cube能够支持"e"消息和"d"消息,这两类消息分别实现了“加密”和“解密”功能。这样的话,我们不仅可以对任意字符串进行加密,还能对Cube加密的任何字符串进行解密。下面有一个简单的例子:

 
 
 
 
  1. ~ » ncat 192.168.0.222 62910 
  2. [...] 
  3. e:TEST^M 
  4. E:kvJcZ8bVAoyXaE7gK+q2Ug== 
  5. d:kvJcZ8bVAoyXaE7gK+q2Ug==^M 
  6. D:TESTAAAAAAAAAAAAAAAAAA== 

注意:Cube要求命令必须以“\ r \ n”结尾,否则它不会给予响应。为此,在netcat / ncat这样的工具中发送命令时,不要直接按下RETURN,而应该先按CTRL + v,然后再按RETURN(由行末尾的“^ M”表示)。

该示例展示了Cube是如何对字符串TEST进行加密的,返回的密文是以Base64编码的字符串kvJcZ8bVAoyXaE7gK + q2Ug ==。 为了对这个字符串进行解密,您可以使用它的解密功能,这样就可以得到明文字符串TEST了。字符串的其余部分只是经过编码的空字节(0x00),用于填充密文,使其符合AES块大小的要求。

加密的字符串通常使用Base64编码。下面,让我们看一个真实的例子:为了使用远程登录,我们需要设置用户名和密码。从Cube发送到远程系统的明文请求如下所示:

 
 
 
 
  1. H:KMD1016788,099c3e,0113 
  2. B:FOOBAR1,5a8a4a5d3c1bd612b8bf1e2fecf609f7,1,SuperSecret 

第一行包括的内容是序列号、RF地址和固件版本。 第二行包括用户名(FOOBAR1)、MD5哈希值,数字(1)和实际密码(SuperSecret)。MD5哈希值是 password||serial_number的哈希值:

 
 
 
 
  1. ~ » echo -n "SuperSecretKMD1016788"|openssl md5 
  2. (stdin)= 5a8a4a5d3c1bd612b8bf1e2fecf609f7 

为了将这个有效载荷发送到远程系统,我们必须进行Base64编码:

 
 
 
 
  1. ~ » echo -n "H:KMD1016788,099c3e,0113 
  2. B:FOOBAR1,5a8a4a5d3c1bd612b8bf1e2fecf609f7,1,SuperSecret"|base64 -w0 
  3. SDpLTUQxMDE2Nzg4LDA5OWMzZSwwMTEzCkI6Rk9PQkFSMSw1YThhNGE1ZDNjMWJkNjEyYjhiZjFlMmZlY2Y2MDlmNywxLFN1cGVyU2VjcmV0 

然后进行加密:

 
 
 
 
  1. e:SDpLTUQxMDE2Nzg4LDA5OWMzZSwwMTEzCkI6Rk9PQkFSMSw1YThhNGE1ZDNjMWJkNjEyYjhiZjFlMmZlY2Y2MDlmNywxLFN1cGVyU2VjcmV0^M 
  2. E:kGxTXPZVm8CQGcurInyvX3z4C+6zKKKcuS8Wp259XC1yKUfN8tFIfRt0s3qRliIcUGSAcuhuDzl7fpT6fWOnyysSxk9TG1cXtrcVkeNWUzgeO5poXjS5tJlXWgV64ibG 

我们现在可以将该Base64字符串复制到Burpsuite的中继器中,进行Base64解码(选中它,然后按CTRL + Shift + b),并将其发送到服务器,具体如下图所示:

如图所示,服务器返回了一个200 OK响应,说明我们的请求成功了。之后,我们就可以使用用户名FOOBAR1和密码SuperSecret登录到http://smarthome.md.de/的Web界面了。 为了给响应消息进行解密,我们可以对响应的主体进行Base64编码(只选中响应的主体,并按CTRL + b),然后将其发送到Cube,利用其解密功能进行处理:

 
 
 
 
  1. d:QAINuzPCglmG1nNNI/ylrbV6AXKdtBQbkNXT/pMobpXSeuP6/tZtCIq8GD5YSHjK^M 
  2. D:aTowMDAwNTFiMSwwMDAwMDAwMCxmZmZmZmZmZg0KYjpPSw0KAAAAAAAAAAAAAAAA 

然后,对得到的Base64字符串进行解码:

 
 
 
 
  1. ~ » echo -n "aTowMDAwNTFiMSwwMDAwMDAwMCxmZmZmZmZmZg0KYjpPSw0KAAAAAAAAAAAAAAAA"|base64 -d 
  2. i:000051b1,00000000,ffffffff 
  3. b:OK 

这里的重要问题是:该设备是如何加密该字符串的,加密密钥是什么? 当谈论AES加密时,你必须弄清楚:

使用的密钥大小是多少? AES支持128、192和256位密钥。

使用什么操作模式?

根据操作模式:初始化向量(IV)是什么?

第一个问题很容易回答:在供应商页面上,他们说它使用的是AES-128。 那么操作模式是什么呢? 知道了它,我们就可以加密任意字符串。 最基本的操作模式是ECB:每个16字节块都被独立加密,对于它来说,如果对明文加密两次后会得到相同的密文。 我已经通过字符串( 16 * “\xff”的Base64编码)进行了测试,这个字符串的大小正好等于AES密码的块大小:

 
 
 
 
  1. e://///////////////////w==^M 
  2. E:XQfNd8PcLZgnJbwGTuTx5A== 
  3. e://///////////////////w==^M 
  4. E:XQfNd8PcLZgnJbwGTuTx5A== 

我们可以看到,对相同的明文(///////////////////// w ==)加密两次,得到的密文是一致的,那么这可能意味着使用的是ECB 。 但是,让我们看看多个块是否是单独进行加密的。 以下示例将会加密32 *“\ xff”(两个块):

 
 
 
 
  1. e://///////////////////w==^M 
  2. E:XQfNd8PcLZgnJbwGTuTx5A== 
  3. e://////////////////////////////////////////8=^M 
  4. E:XQfNd8PcLZgnJbwGTuTx5LM36fXWGGUjgVLWxtzwCgo= 

如果它使用了ECB模式,那么密文应包含两份先前看到的字节序列:

 
 
 
 
  1. cryptotest » echo -n "XQfNd8PcLZgnJbwGTuTx5A=="|base64 -d |xxd 
  2. 00000000: 5d07 cd77 c3dc 2d98 2725 bc06 4ee4 f1e4 ]..w..-.'%..N... 
  3. cryptotest » echo -n "XQfNd8PcLZgnJbwGTuTx5LM36fXWGGUjgVLWxtzwCgo="|base64 -d |xxd 
  4. 00000000: 5d07 cd77 c3dc 2d98 2725 bc06 4ee4 f1e4 ]..w..-.'%..N... 
  5. 00000010: b337 e9f5 d618 6523 8152 d6c6 dcf0 0a0a .7....e#.R...... 

我们可以在hexdump中看到,第一个16字节的确与以前的加密结果一致,但是第二个块是完全不同的。 实际上,ECB加密结果应该是这样的:

 
 
 
 
  1. cryptotest » xxd plain_16 
  2. 00000000: ffff ffff ffff ffff ffff ffff ffff ffff ................ 
  3. cryptotest » xxd plain_32 
  4. 00000000: ffff ffff ffff ffff ffff ffff ffff ffff ................ 
  5. 00000010: ffff ffff ffff ffff ffff ffff ffff ffff ................ 
  6. cryptotest » openssl enc -aes-128-ecb -in plain_16 -nosalt -nopad -k TEST |xxd 
  7. 00000000: cb30 66d5 3db8 89f6 da4b 5831 d29c 6b9f .0f.=....KX1..k. 
  8. cryptotest » openssl enc -aes-128-ecb -in plain_32 -nosalt -nopad -k TEST |xxd 
  9. 00000000: cb30 66d5 3db8 89f6 da4b 5831 d29c 6b9f .0f.=....KX1..k. 
  10. 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哈希值,所以,我们不妨尝试用这个密钥来解密密文:

 
 
 
 
  1. ~ » echo -n "XQfNd8PcLZgnJbwGTuTx5LM36fXWGGUjgVLWxtzwCgo="|base64 -d |openssl enc -aes-128-cbc -d -nopad -nosalt -K 98bbce3f1b25df8e6894b779456d330e -iv 00 |xxd 
  2. 00000000: c975 1589 ed36 536c c975 1589 ed36 536c  .u...6Sl.u...6Sl 
  3. 00000010: ffff ffff ffff ffff ffff ffff ffff ffff  ................ 

棒极了!您可以看到,第二个块已正确解密了。 实际上,第一个块应该是相同的,但它看起来却是完全随机的。 在上一个命令中,我使用了空字节来作为IV(-iv 00)。但是,不要忘了,在解密CBC模式中的最后一个块之后,需要将最后一个块与IV进行XOR。这样就好理解了:由于第一个块(在解密期间将被最后处理)与一个错误的值进行了异或运算,所以才导致了不同的明文。

然而,在这种情况下获得IV是相当容易的,因为我们知道明文。我们只要将前面得到的第一个块与明文(即16 *“\ xff”)进行异或运算,就能得到正确的IV了。现在,请打开一个Python shell,只需要进行如下所示的操作即可:

 
 
 
 
  1. >>> hex(0xc9751589ed36536cc9751589ed36536c^0xffffffffffffffffffffffffffffffff) 
  2. '0x368aea7612c9ac93368aea7612c9ac93L' 

现在,让我们用这里的IV再次对密文进行解密:

 
 
 
 
  1. ~ » echo -n "XQfNd8PcLZgnJbwGTuTx5LM36fXWGGUjgVLWxtzwCgo="|base64 -d |openssl enc -aes-128-cbc -d -nopad -nosalt -K 98bbce3f1b25df8e6894b779456d330e -iv 368aea7612c9ac93368aea7612c9ac93 |xxd 
  2. 00000000: ffff ffff ffff ffff ffff ffff ffff ffff  ................ 
  3. 00000010: ffff ffff ffff ffff ffff ffff ffff ffff  ................ 

如你所见,我们的明文已经完全恢复了!我们还可以解密远程服务器的响应:

 
 
 
 
  1. ~ » echo -n "QAINuzPCglmG1nNNI/ylrbV6AXKdtBQbkNXT/pMobpXSeuP6/tZtCIq8GD5YSHjK"|base64 -d |openssl enc -aes-128-cbc -d -nopad -nosalt -K 98bbce3f1b25df8e6894b779456d330e -iv 368aea7612c9ac93368aea7612c9ac93     
  2. i:000051b1,00000000,ffffffff 
  3. b:OK 

现在,我们已经掌握了在Cube和远程服务器上对字符串进行加密和解密的所有秘密。同时,我还实现了一个小的Python脚本,不仅使得加密/解密字符串变得更加简单,同时还能完成适当的填充操作。

网络发现的自动化

在我看来,分析未知设备的一个重要部分,就是让其他人也能使用已获得的信息,以支持他人的进一步研究,或能够让人们用通用工具来发现这样的设备。当涉及到发现网络上的设备时,我选择的通用工具是Nmap。除了纯端口扫描之外,它还提供了大量已知服务的签名,同时,我们还可以通过NSE脚本来对其功能进行扩展。

NSE脚本是用Lua语言编写的,而Lua又是一种相当简单和易于理解的脚本语言。 开始编写自己的脚本时,最简单的方法是就是学习现有的脚本(脚本通常位于/usr/share/nmap/scripts目录中,或者在线查找)。例如,对于身份请求来说,我们只需要发送一个UDP包,然后检索响应的有效载荷即可。一个浅显易懂例子是daytime.nse脚本,具体如下所示:

 
 
 
 
  1. portrule = shortport.port_or_service(13, "daytime", {"tcp", "udp"}) 
  2. action = function(host, port) 
  3.   local status, result = comm.exchange(host, port, "dummy", {lines=1}) 
  4.   if status then 
  5.     return result 
  6.   end 
  7. 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地址和固件版本 。

 
 
 
 
  1. H:KMD1016788,099c3e,0113,00000000,7ee2b5d7,00,32,100408,002c,03,0000 
  2. [...] 

所以,我们的脚本只需要连接到该端口,获得响应并解析值KMD1016788(序列号)、099c3e(RF地址)和0113(固件版本),代码具体如下所示:

 
 
 
 
  1. local shortport = require "shortport" 
  2. local stdnse = require "stdnse" 
  3. description = [[ 
  4. ]] 
  5. author = "CHANGEME" 
  6. license = "Same as Nmap--See https://nmap.org/book/man-legal.html" 
  7. categories = {"discovery", "safe"} 
  8. portrule = shortport.portnumber(0, "tcp") 
  9. action = function(host, port) 
  10. end 

在上面的代码的基础之上,可以继续添加所需的功能。 因为我们只需要连接到一个端口来获取响应而不发送任何东西,所以最简单的方法是使用一个简单的套接字。有了Nmap后,利用NSE脚本进行socket通信变得异常轻松:

 
 
 
 
  1. local sock = nmap.new_socket() 
  2. local status, err = sock:connect(host, port, "tcp") 
  3. if not status then 
  4.   stdnse.debug1("%s", err) 
  5.   return 
  6. end 
  7. local status, data = sock:receive() 
  8. if not status or not data then 
  9.   stdnse.debug1("%s", "Could not receive any data") 
  10.   return 
  11. end 

这样就可以在变量ret中接收响应了,然后解析该变量,就能提取所需的信息了:

 
 
 
 
  1. local output = stdnse.output_table() 
  2. local serial, rf_address, firmware 
  3. 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 
  4.     output["MAX Serial:"] = serial 
  5.     output["RF Address"] = rf_address 
  6.     output["Firmware Version"] = firmware 
  7. end 

现在,我们就有了一个输出表,其中包含了需要返回给用户的所有信息。正如前面说过的一样,为此只需在action的末尾放上一个“return output”即可。 完整的脚本可以在这里下载。

我们可以测试该脚本,检查是否能够正常工作:

 
 
 
 
  1. max-cube/nse » nmap --script maxcube-info.nse -Pn -p 62910 192.168.0.222 
  2. Starting Nmap 7.12SVN ( https://nmap.org ) at 2016-04-08 01:11 CEST 
  3. Nmap scan report for 192.168.0.222 
  4. Host is up (0.0051s latency). 
  5. PORT      STATE SERVICE 
  6. 62910/tcp open  unknown 
  7. | maxcube-info:  
  8. |   MAX Serial:: KMD1016788 
  9. |   RF Address: 099c3e 
  10. |_  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等项目贡献代码,以便帮助更多的人。

网页题目:物联网设备安全分析之MAX!CubeLANGateway篇
网页网址:http://www.shufengxianlan.com/qtweb/news5/388355.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联