首页
社区
课程
招聘
[翻译]了解智能家居,揭示简单物联网产品的漏洞
2018-10-1 11:50 8036

[翻译]了解智能家居,揭示简单物联网产品的漏洞

2018-10-1 11:50
8036

万物互联便利了人们的生活。如果想要远程打开和关闭电灯或电器,并且在线监视他们,使用“智能插座”—— wifi 连接的插座是一种简单的方法。但是如果 IoT 设备没有得到妥善的保护,也会成为一种攻击向量。


McAfee实验室威胁发现团队致力于发现软件和硬件中的安全问题,帮助开发人员为企业和消费者提供更安全的产品。我们最近调查了Belkin生产的产品。我们在Wemo Insight智能插座中发现了未报告的 libUPnPHndlr.so 库文件缓冲区溢出漏洞。该溢出会允许攻击者执行远程代码,cve编号为 CVE-2018-6692。根据我们负责任的披露政策,我们在5月21日向Belkin报告了此研究。


这个漏洞能否导致有效的攻击?智能插座本身的影响很小。攻击者可以关闭开关或者最坏情况下可以使开关过载。但是如果这个插座与其他设备联网,潜在的威胁会增加。这个插座会成为更大攻击的入口点。在这篇报告中,我们将会研究一种可能的攻击。

探索攻击面

根据说明书建议,团队使用Wemo手机应用程序来设置智能插座。我们可以远程开关插座。接下来,我们测试了软件,包括端口扫描,监控正常网络流量,阅读在线研究。Wemo监听通用即插即用(UPnP)协议49152和49153端口,使用TCP协议。手册,反汇编镜像,通用编程语言(GPL)都可以在网上找到;他们还提供了CPU架构、操作系统和应用程序相关的信息。


我们转向研究硬件并拆解了设备。我们在主板上识别出了芯片,发现用于与设备通信的标头,并且从闪存中取出了内存。我们通过在线研究得到了主板上的每一个芯片的数据表。


我们在电路板上找到了通用异步接收器-发送器(UART)焊盘,并通过文档确认了它们。我们将电线焊接到这些头部以发现它们是否用于传输。为了测试与设备的通信,我们使用了Exodus XI Breakout板,如下所示:

在强制设定波特率之后,我们可以通过UART接口获得调试信息。UART还提供了登陆提示,但是,无论在线调查还是简单的猜解都无法获得有效密码。


提取固件并分析

电路板上发现的闪存芯片是Maxronix MX25L12835F,由flashrom支持,flashrom是一种众所周知的用于提取固件的开源工具。用flashrom和XI Breakout板,我们从Wemo设备中提取了固件。在我们获得设备附带的原始固件映像后,我们使用Wemo移动应用程序对其进行了更新。设备更新后,我们再次从设备中提取固件,为我们提供第二个镜像。我们对新固件进行了基本的健全性检查,以确保我们早期的软件侦察信息没有改变。


提取固件后,我们使用开源二进制分析工具binwalk分析了固件。Binwalk从固件中提取文件系统以供进一步检查。访问文件系统可以让我们查看系统配置和访问二进制文件。

发现漏洞

网络或远程漏洞比本地漏洞更危险,因此我们仔细研究了在本地网络上监听的UPnP端口。在此测试阶段,我们的首席分析师正在参加Exodus智能硬件漏洞利用课程。Elvis Collado(@ b1ack0wl)是讲师之一,他正在开发一个UPnP模糊测试器为我们提供帮助。使用此工具,我们对开放的UPnP端口进行模糊测试,同时监控Wemo上的UART接口。过了一会儿,我们看到了UART界面的崩溃。

11:37:16.702 stuntsx0x46ac6 STUN client transaction destroyed
sending SIGSEGV to wemoApp for invalid write access to
464d4945 (epc == 2ac1fb58, ra == 2ac1fccc)
Cpu 0
$ 0 : 00000000 00000001 0000006d 464d4945
$ 4 : 31d2e654 31d2e770 00000003 00000001
$ 8 : 0000007c fffffff8 00000007 00000002
$12 : 00000200 00000100 00000807 00000800
$16 : 31d2e6f0 31d2e898 004a1cb8 00000002
$20 : 31d2e638 31d2e6c0 004a1388 31d2e640
$24 : 00000400 2ac1fb30
$28 : 2ac77d40 31d2e600 31d2e648 2ac1fccc
Hi : 00000008
Lo : 00000000
epc : 2ac1fb58 Tainted: P
ra : 2ac1fccc Status: 0100fc13 USER EXL IE
Cause : 8080000c
BadVA : 464d4945
PrId : 0001964c
Modules linked in: softdog rt_rdm rt2860v2_ap(P) raeth
Process wemoApp (pid: 2157, threadinfo=80fa0000, task=802c87f0)
Stack : 2a0000d0 fffffffe 31d2e6f0 31d2e770 31d2e76f 31d2e6f0 31d2e6f0 31d2e770
00000000 31d2e604 00000000 00000000 2ac77d40 00000000 4f464751 4a484d4c
4e444241 47454f49 50464658 45414d42 43445044 464d4945 5552414c 46495048
4b524141 41445a4f 44534e4a 4e4e494c 44434357 494a4855 44515455 44494b45
55584a44 584e4f52 545a5247 51545954 595a4c42 4e594a45 484f5158 46474944
…
Call Trace:

Code: 80a20000 50480004 a0600000 <5440fffa> a0620000 a0600000 10a00006 24840004 24a50001
thready: Destructor freeing name “ChildFDTask”.
Aborted
在多次重复并密切观察实验后,我们确定崩溃是由以下数据包引起的:
POST /upnp/control/basicevent1 HTTP/1.1
Host: 192.168.225.183:49154
User-Agent: python-requests/2.9.1
Accept: */*
Connection: keep-alive
SOAPAction: “urn:Belkin:service:basicevent:1#UpdateInsightHomeSettings”
Content-Type: text/xml
Accept-Encoding: gzip, deflate
Content-Length: 3253

<?xml version=”1.0″ ?><s:Envelope s:encodingStyle=”http://schemas.xmlsoap.org/soap/encoding/” xmlns:s=”http://schemas.xmlsoap.org/soap/envelope/”><s:Body><b1ack0wl_ns:UpdateInsightHomeSettingsxmlns:b1ack0wl_ns=”urn:Belkin:service:basicevent:1″><EnergyPerUnitCost>210</EnergyPerUnitCost><Currency>236</Currency><EnergyPerUnitCostVersion>KWWZWIVYBQZKDGSSAAPBCQVQQFAVYZEOEUFIDXXQPDYGESTOD
GIJFERXZNMYAFJQLUZPSIJXFQSPADCRIVHDAJLLPQMPLAVECIQUWLXDLIGPLBKCROGPOCVUI
KTSLIIXULOEBVFKWIERCFGHWHCBBDLWFBKBZXAVGRKTDALDNRPOFQJDXAEOC(…snip…)XHU
OUZPCHUBFGLLWSJBFYFOMCGZZMJIQIUVCDETFBRBZVDVKNBVZFBRSVBSZPAYKZYNQZEQPDV
DWSZNDUPUDCPAVWNFBFBTYMXTBNCWTBJPKORUBHBSCQBPOPOBZNVADMGWRI
</EnergyPerUnitCostVersion></b1ack0wl_ns:UpdateInsightHomeSettings></s:Body></s:Envelope>

由于空间原因,一些有效载荷已被删除。(“EnergyPerUnitCostVersion”中的原始数据为2,828个字符。)在分析崩溃数据和数据包之后,发现这似乎是一个标准的缓冲区溢出,数据溢出到栈上。我们继续进行模糊测试,着力于“EnergyPerUnitCost”字段,发现只需32个字符便能使应用程序崩溃。


虽然崩溃转储为我们提供了很多信息,但仍然有很多我们不知道的。例如,崩溃发生在“WemoApp”中并为我们提供了一个偏移量,但这个库的基地址是什么?栈上的什么被覆盖了?如果在运行期间不访问应用程序,则很难回答这些问题。因为我们之前获得了文件系统,所以我们可以静态分析WemoApp二进制文件; 但我们仍然无法轻易确定崩溃的确切位置。


要回答这些问题,我们可以采取两种方法。我们可以虚拟化Wemo固件或二进制文件以继续测试; 或者如果我们可以确定UART接口上的root密码,我们就有可能在设备上进行调试。通常,虚拟化固件并不简单,有时会导致测试结果不准确。最好在设备上进行调试。通过我们在侦察过程中发现的所有信息,我们似乎可以绕过root密码。(我们确实花了一些时间尝试虚拟化WemoApp - 但没有成功)。 

绕过root密码

从提取的文件系统中,我们了解到Wemo运行嵌入式Linux系统OpenWRT,用户帐户信息保存在标准的/ etc / passwd或/ etc / shadow文件中。我们从/ etc / passwd中提取了root密码的哈希值,并将其提交给了一个破解平台。这种方法在合理的时间内证明是无效的。


凭借我们读取芯片的能力,我们有可能去写入芯片。除非校验和或验证是在固件上完成的,否则我们可以使用已知密码替换/ etc / passwd文件。


为了验证这个理论,我们需要重新打包固件。由于wemo的GPL是公开的,我们选择使用与开发人员相同的工具。通过GPL,我们使用Izma编译了相同版本的squash tools 3.0,并使用修改后的/ etc / passwd文件重新打包了固件文件系统。我们进行了填充,确保固件与其原始版本大小相同。然后,我们使用了“dd”指令,将新文件系统段插入到二进制固件中。在这个过程中,我们发现使用binwalk提取的固件无法正确的重新打包。我们使用“dd”命令加上binwalk提供的信息提取固件的正确部分以进行重新打包。


有了新固件二进制文件之后,我们使用XI Breakout板和flashrom 将固件写入板上的闪存芯片中。在重启设备后,我们可以用新密码登录。

分析崩溃

有了wemo的root权限之后,我们可以在对UPnP模糊测试期间收集更多崩溃信息。首先,我们需要编译深入分析这种特定体系结构所需的工具。使用GPL,我们为设备编译了gdbserver和gdb。Wemo有很多预装工具,例如“wget”,使添加文件更加简单。我们从 /tmp 目录下载并运行了这些工具


经过大量的尝试之后,我们无法直接运行或远程运行gdb。因此,我们使用gdbserver和IDA pro进行所有调试。连接调试器后,我们发送导致崩溃的数据包并查看崩溃的确切位置。在0x2AC15B98处出现了一个段错误。从linux的proc目录可以确定这个地址位于libUPnPHndlr.so中。

2abf3000-2ac4d000 r-xp 00000000 1f:02 82 /rom/lib/libUPnPHndlr.so

由于崩溃是由UPnP数据包引起的,因此在此库中查找崩溃是合乎逻辑的。使用基址0x2abf3000,我们计算出IDA中静态分析的偏移量为0x22b98。在这个地址,我们发现了以下内容:
LOAD:00022B70  # =============== S U B R O U T I N E =======================================

LOAD:00022B70

LOAD:00022B70

LOAD:00022B70                 .globl TokenParser

LOAD:00022B70 TokenParser:                             # CODE XREF: ProcessEnergyPerunitCostNotify+84↓p

LOAD:00022B70                                          # DATA XREF: LOAD:00004210↑o …

LOAD:00022B70                 beqz    $a1, locret_22BC0

LOAD:00022B74                 move    $a3, $zero

LOAD:00022B78                 move    $a3, $zero

LOAD:00022B7C                 b       loc_22BB4

LOAD:00022B80                 li      $t0, 0x7C  # ‘|’

LOAD:00022B84  # —————————————————————————

LOAD:00022B84

LOAD:00022B84 loc_22B84:                               # CODE XREF: TokenParser+28↓j

LOAD:00022B84                 addiu   $a1, 1

LOAD:00022B88                 addiu   $v1, 1

LOAD:00022B8C

LOAD:00022B8C loc_22B8C:                               # CODE XREF: TokenParser+48↓j

LOAD:00022B8C                 lb      $v0, 0($a1)

LOAD:00022B90                 beql    $v0, $t0, loc_22BA4

LOAD:00022B94                 sb      $zero, 0($v1)

LOAD:00022B98                 bnezl   $v0, loc_22B84

LOAD:00022B9C                 sb      $v0, 0($v1)

LOAD:00022BA0                 sb      $zero, 0($v1)

LOAD:00022BA4

LOAD:00022BA4 loc_22BA4:                               # CODE XREF: TokenParser+20↑j

LOAD:00022BA4                 beqz    $a1, locret_22BC0

LOAD:00022BA8                 addiu   $a0, 4

LOAD:00022BAC                 addiu   $a1, 1

LOAD:00022BB0                 addiu   $a3, 1

LOAD:00022BB4

LOAD:00022BB4 loc_22BB4:                               # CODE XREF: TokenParser+C↑j

LOAD:00022BB4                 slt     $v0, $a3, $a2

LOAD:00022BB8                 bnezl   $v0, loc_22B8C

LOAD:00022BBC                 lw      $v1, 0($a0)

LOAD:00022BC0

LOAD:00022BC0 locret_22BC0:                            # CODE XREF: TokenParser↑j

LOAD:00022BC0                                          # TokenParser:loc_22BA4↑j

LOAD:00022BC0                 jr      $ra

LOAD:00022BC4                 move    $v0, $a3

LOAD:00022BC4  # End of function TokenParser

因为开发人员将二进制文件保存为unstripped(未去除符号表),我们将此函数命名为TokenParser。段错误发生在分支指令处,但是,在MIPS架构,延迟槽指令在分支发生前执行。因此,0x22B9C处的指令造成了崩溃。在这里,应用程序尝试加载$v1中的地址,将其存入$v0。查看寄存器之后,我们发现数据包中XML标签” EnergyPerUnitCostVersion”的数据在$v1中,产生“无效的写访问”的段错误。


通过静态分析函数,该函数似乎是将数据从一个部分复制到另一个部分,寻找三次“0x7C”也就是“|”字符。如果找不到“|”,它会一直复制到静态定义的缓冲区中。为了完全理解为什么会发生覆盖,让我们在执行函数时看一下堆栈:

2EF17630 2AC692F0 MEMORY:2AC692F0
2EF17634 00000000 MEMORY:saved_fp
2EF17638 34333231 MEMORY:34333231 ← 先前复制的数据
2EF1763C 00000035 MEMORY:retaddr+31  ← 下一个字节将写入0x2EF1763D
2EF17640 00000000 MEMORY:saved_fp  ← 复制准备的内存
2EF17644 00000000 MEMORY:saved_fp
2EF17648 00000000 MEMORY:saved_fp
2EF1764C 00000000 MEMORY:saved_fp
2EF17650 2EF17638 MEMORY:2EF17638 ← 开始写入该地址; 可以被覆盖


当函数将数据复制到堆栈时,它最终会超过原始缓冲区地址。一旦该地址被覆盖,该函数就会尝试以新值写入下一个字节,在这种情况下是无效地址。这种溢出为攻击者提供了两个可利用的向量:write-what-where这一条件允许攻击者将数据写入内存中的任意位置; 通过继续覆盖堆栈上的数据,攻击者可以覆盖$RA寄存器或函数返回地址,从而为攻击者提供对执行流程的控制。


编写漏洞利用

既然我们了解了漏洞,我们可以利用它吗?因为这是一个标准的缓冲区溢出,我们需要回答两个问题。堆栈上有多少可用空间,是否有任何“坏”字节无法写入堆栈?为了确定可用空间,通过用有效地址修复栈上被覆盖的地址,我们可以检查有效负载有多少进入堆栈。我们发现只有91个字节可以写入堆栈。


下一步是确定是否存在任何“坏”字节。在运行一些测试之后,我们注意到只有ASCII字符才能进入堆栈。在执行易受攻击的代码之前,数据包由开源XML解析器“mxml”解析。该库遵循允许标记之间仅存在ASCII和Unicode字符的标准。


这个标准对于shellcode和面向返回的编程(ROP)技术很不利,因为内存地址和shellcode使用的几乎都是不可读的字符。我们应该使用某些技术利用栈上的空间;然而,由于在XML过滤字符上的严格限制,最好使用已经加载进内存的函数。一种不需要大量shellcode的方法是使用“return to libc”攻击来执行system命令。由于system调用通常将字符串作为参数,因此可能会通过过滤器。因为Wemo没有使用地址随机化,理论上可以进行system调用而无需通过XML过滤器传递额外的shellcode。


我们还面临一个重大挑战:只有全是ascii码的地址才能通过XML过滤器。这极大的限制了找到可用gadget的可能性。我们使用IDA查看libc和system加载到内存中的位置,并找到了两个实例:在0x2B0C0FD4处的libuClibc-0.9.33.2.so和0x2AD104F4处的libpthread-0.9.33.2.so。但是,这些地址都不符合通过XML过滤器的要求。因此,即使我们可以创建ROP链,我们也无法仅在数据包中发送system地址。


包含有坏字符的地址不是漏洞利用的新问题。一个最常见的绕过方法是通过对ROP gadgets 进行加法或减法在寄存器中构造需要的地址并调用。但是,我们面临的限制在于由于XML过滤器的存在,哪些操作数可以用于此加法或减法方程。


在研究过内存布局之后,我们发现libuClibc-0.9.33.2.so位于一个可以绕过XML过滤器的地址。幸运的是,这是一个大型库,提供了一个很大的地址列表,这是此空间中唯一的满足条件的库。通过这一发现,我们开发了一个工具辅助形成exploit。这个工具从可用的内存地址提取所有可能的ROP gadgets,并确定加法或减法是否可以仅使用将绕过过滤器的值,调用内存中找到的两个系统调用之一。libuClibc-0.9.33.2.so中的system地址0x2B0C0FD4没有任何可用的操作数。但是,0x2AD104F4有可用操作数。我们发现了几个“防过滤”操作数,当加在一起时等于0x2AD104F4。


我们使用我们的工具输出所有可以绕过过滤的可能的ROP gadgets,构建ROP链,使用加法指令为system创建最终地址并将其存储在$s0中。加法指令之后,另一个gadget将system地址移动到$t9并调用system。这个最后的gadget还将可以从堆栈控制的地址移动到保存system调用参数的寄存器中。整个ROP链只包含三个gadget,可轻松适应缓冲区溢出提供的堆栈空间。


拼凑所有东西

之前我们发现了两种可以与此漏洞一起使用的攻击技术:write-what-where,并覆盖堆栈上的返回地址。发送的每个数据包可以使用每种技术一次。要获取system调用的参数,我们必须使用write-what-where将参数放在可写的内存地址中,并将此地址传递给system。幸运的是,这个易受攻击的应用程序预留了大量不会使用的可写内存,并且在我们绕过过滤器的受限地址可访问的范围内。不幸的是,调用system的ROP链需要使用write-what-where来处理其中一个ROP gadgets中的额外指令。这意味着执行漏洞需要两个数据包:一个用于将system参数写入内存,另一个用于调用system。因此第一个包无痕地存在且不造成程序崩溃很重要。


干净地执行的一种方法是在有效负载内使用三个位置正确的管道(“|”)来停止写入并在适当的时间退出TokenParser。不重写RA指针也很重要,这样程序可以在收到数据包后继续正常执行。然后发送包含ROP链system调用的第二个数据包,其中包含前一个数据包写入的参数的地址。


payload

通过发现可以调用system的有效ROP链,我们必须决定应该使用哪种system调用。因为system以root身份执行,所以我们可以完全控制设备。我们的研究表明该设备安装了许多Linux命令。我们之前使用wget将gdbserver复制到设备。攻击者还可以从system调用wget来下载并执行任何脚本。我们进一步探索了已安装的应用程序并发现了NetCat,它可能允许攻击者编写脚本来创建反向shell。攻击者可以使用wget下载脚本,并执行包含NetCat命令的脚本来创建反向shell。我们测试并证明以root身份打开反向shell是一种简单有效的方法。攻击者可以选择许多其他方法来利用此漏洞并执行代码。下面的视频展示了反向shell的漏洞利用。

视频链接:https://players.brightcove.net/21712694001/Sypf9RNV_default/index.html?videoId=5823570


为了说明,团队编写了一个攻击场景。插件被攻破后,它可以使用内置的UPnP库在网络路由器中形成漏洞。这个洞为攻击者创建了一个后门,可以远程连接,而不会在网络上被注意到。在下面的视频中,我们使用远程shell来控制连接到网络的TCL智能电视。电视上的Roku API实现使用简单的未加密HTTP GET / POST请求来发出命令,并且不对发送这些命令的机器进行身份验证,从而使远程控制变得很容易。使用Wemo作为中间人,攻击者可以打开和关闭电视,安装或卸载应用程序,以及访问任意在线内容。智能电视只是使用Wemo攻击其他设备的一个例子。由于攻击者已经在网络上建立了立足点并且能够打开任意端口,因此连接到网络的任何计算机都处于危险之中。由于攻击可以通过Wemo进行,并且使用此漏洞利用生成的端口映射在路由器的管理页面中不可见,因此攻击者的足迹仍然很微小并且难以检测。

视频链接:https://players.brightcove.net/21712694001/Sypf9RNV_default/index.html?videoId=5823567767001


结论

诸如CVE-2018-6692之类的发现强调了所有设备上安全编码的重要性。从安全角度来看,物联网设备经常被忽视; 这可能是因为许多用于看似无害的目的,例如简单的家庭自动化。但是,这些设备运行操作系统并且需要与台式计算机一样多的保护。如我们发现的漏洞可能成为攻击者进入并破坏整个业务网络所需的立足点。


McAfee高级威胁研究团队的一个目标是在当今复杂且不断发展的环境中识别和阐明各种威胁。通过分析和负责任的披露,我们的目标是引导产品制造商采取更全面的安全保护。


原文链接:https://securingtomorrow.mcafee.com/mcafee-labs/insight-into-home-automation-reveals-vulnerability-in-simple-iot-product/

翻译:看雪翻译小组  欢歌笑语

校对:看雪翻译小组  一壶葱茜



[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

最后于 2019-1-11 19:36 被kanxue编辑 ,原因:
收藏
点赞1
打赏
分享
最新回复 (4)
雪    币: 355
活跃值: (10)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
Victorgg 2018-10-1 11:58
2
0
喜欢IOT方面的文章,谢谢!
雪    币: 5676
活跃值: (1303)
能力值: ( LV17,RANK:1185 )
在线值:
发帖
回帖
粉丝
holing 15 2018-10-1 23:29
3
0
最近也准备研究这个,感谢分享
最后于 2018-10-14 19:01 被holing编辑 ,原因:
雪    币: 7074
活跃值: (3468)
能力值: ( LV12,RANK:340 )
在线值:
发帖
回帖
粉丝
bxc 6 2018-10-2 12:29
4
0
感觉IOT安全以后会很有前景。
雪    币: 11716
活跃值: (133)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
junkboy 2019-1-11 20:09
5
0
#寻宝大战#祝看雪19岁快乐!
游客
登录 | 注册 方可回帖
返回