嘶吼RoarTalk 09-17
是时候对物联网医疗设备下手了:对心脏起搏器生态系统安全性研究(part2)
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_keji1.html

 

对心脏起搏器生态系统安全性研究(part1)

0x01 对 HMU 的网络分析和数据提取

在本文中,我们将研究 HMU 与制造商的服务器之间的通信接口。如引言中所述,CardioMessenger II-S HMU 有两种形式:T-Line 和 GSM。顾名思义,它们会延迟与后端服务器的通信方式。在本文中,我们将首先介绍 T-Line 版本,并展示如何利用缺乏相互认证的功能从设备中提取数据,然后在 GSM 版本上实现相同的目的。

1. 分析 Telephone Line 界面

在本系列的第二篇有关查找调试接口的文章中,我们还研究了微控制器和调制解调器之间的通信链接。发生的是微控制器向调制解调器发送 AT 命令以对其进行配置,更具体地说,它要求调制解调器使用点对点协议(PPP)建立与 Internet 服务提供商的连接。这是使用 PAP 作为身份验证类型完成的,因此在开始通信之前也要设置凭据。下面的清单显示了微控制器发送给调制解调器的 AT 命令的完整交换。

[ 2019   -03   -30   11:43:24 ]   at   atii5#vversion   AT#MCOUNTRY?   at   AT+WOPEN=1   AT#DIALSELECT=1   AT#AUTHENT=PAP    at#atcmd=0,"-STE=7"    at#atcmd=1,"+A8E=6,5,0,1,0,0"    at#atcmd=2,"X3"   at   AT#DIALN1="T9W [ REDACTED ] "    AT#ISPUN=" [ REDACTED ] @cm3-homemonitoring.de"    AT#ISPPW=" [ REDACTED ] "   at#atcmd=0,"-STE=7"   [ 2019-03-30   11:43:26 ]   AT#CONNECTIONSTART    [ 2019-03-30   11:43:28 ]   at#connectionstop

这里要注意的一件有趣的事是,连接在启动后就立即关闭,这是有原因的:HMU 没有插入任何电话线!现在,进行测试时没有电话线可用,此外,目标是无论如何都不与后端服务器进行交互。

从图 1 可以看出,可以很容易地从 PCB 上卸下调制解调器,因此,我决定使用常规的 USB-to-TTL 适配器简单地将计算机插入电源,并模拟调制解调器。

图 1:CardioMessenger II-S T 线调制解调器

为此,我使用 PySerial 编写了一个小的 python 脚本来与 UART 交互并回复微控制器发送的命令。通过监听合法调制解调器对微控制器的响应,我得到了第一个命令的预期响应,通过查看AT 命令参考,我得到了其余的响应。该 AT#connectionstart 命令希望看到 CONNECT [ BAUDRATE ] 消息,然后是归属于 HMU 的 IP 地址,最后是 OK_Info_PPP 消息。HMU 将在发送下一个命令之前显式等待最后一条消息 AT#TCPSERV。顾名思义,TCPSERV 打开到给定 IP:Port 的 TCP 套接字。在这种情况下,HMU 打开一个指向 172.16.14.1:4242 的套接字。为了响应 TCPSERV 命令,我们发送了 OK_WaitingForData 消息告诉微控制器调制解调器已准备好接收数据(因此已切换到数据模式)。一旦我们发送了该命令,微控制器就会发送一堆十六进制数据。下面的清单显示了完整的交换。

  [ 2019-03-27   22:54:03 ]   OK     [ 2019-03-27   22:54:04 ]   OK     [ 2019-03-27   22:54:05 ]   OK     [ 2019-03-27   22:54:06 ]   OK     [ 2019-03-27   22:54:09 ]   DIALING   >  [ 2019-03-27   22:54:09 ]   TW [ REDACTED ]   >  [ 2019-03-27   22:54:09 ]   CONNECT   115200   >  [ 2019-03-27   22:54:09 ]   172.16.14.80   >  [ 2019-03-27   22:54:09 ]   OK_Info_PPP     [ 2019-03-27   22:54:12 ]   OK_Info_WaitingForData     ----------   Switching   to   data   mode   -------------   显示对地址的引用)。在与与加密错误相关的字符串相关的字符串上应用后,我们得到一个大函数(实际上有两个函数:GetDataFromEncryptionLayer 和 PackToEncryptionLayer)。使用 Ghidra 的反编译器,我们可以看一下重构的 C 代码,其中包含一个有趣的模式:

int   var;   FUNCTION_P ( start_string_A ) ; var   =   FUNCTION_A ( something ) ;   if   ( var   ==   0 )   {                 //   Do   something }   else   {        FUNCTION_P ( error_string_A ) ;   }

FUNCTION_P 显然是 " 类似打印 " 的函数,而 FUNCTION_A 是在 start_string_A 和 error_string_A 中引用的函数。图 2 显示了反编译代码的各个部分,其中最重要的函数和变量已重命名,并带有注释以突出显示协议的不同层。

图 2:反编译的 PackData 函数

可以重复此过程以标识固件中的更多函数,从而更好地理解代码。我不会在这篇文章中提供更多细节,但是论文中提供了更多信息。

综上所述,我们可以确定以下内容:

· 负责打包数据的一般函数(其代码在图 2 中部分暴露)

· 处理每一层封装的函数:

      · PackToMessageLayer

      · PackToCompressionLayer

      · PackToEncryptionLayer

      · PackToTransportLayer

· 使用的压缩格式为 "deflate",这是 RFC 1951 中定义的 " 无损压缩数据格式 "。

· 使用的加密算法:DES,3DES CBC 和 AES CBC。

如果我们查看 PackToEncryptionLayer 的一部分(图 3),我们会看到用于加密的不同算法,以及用来表示它们的 " 操作码 ":6 代表 DES,7 代表 3DES CBC,8 代表 AES。

图 3:反编译的 PackToEncryptionLayer 函数,AES CBC 部分

有了我们掌握的新信息,我们现在可以全面了解此处使用的协议,如图 4 所示:

图 4:通信协议数据包的详细结构

2. 数据解密

调制解调器和 GPRS 数据

现在,我们有解密从 HMU 提取的所有数据,除了 AES 密钥。有了固件,我确信我能理解密钥的来源。如图 4 所示,可以在 PackToEncryptionLayer 函数中标识该密钥,因此可以看到它的来源,我这样做了。我还编写了一个 python 脚本来自动化数据分析过程。

我还不能放弃,因此决定 " 强行使用 " AES 密钥。我的推理如下:" 鉴于板上的组件,必须在执行加密之前的某个时刻将密钥加载到 RAM 中,因此,如果我在执行 PackToEncryptionLayer 函数之前设置一个断点,并转储 RAM 此时,必须在其中包含 AES 密钥。如果在其中,那么我只需要尝试所有可能的键,对于 2MB RAM 来说就是…… 2097152 键,即使在使用 python 的个人笔记本电脑上,也可以测试大约 200 万个键。"

但是下一个问题是:我们如何才能检测出正确的密钥呢?因此,到那时我们知道压缩层已封装在加密层中。我们还知道,压缩层的数据将以一个已知的头开始:(0x9F 压缩层的 " 操作代码 ")+ 0x1F8B(gzip 文件的头)。因此,我在自己的脚本中实现了这一点。出于好奇,我直接查看代码,将获得的密钥与我发现的密钥进行了对照,结果是相同的。我仍然不确定为什么我第一次错了,后来发现我忘记了考虑字节顺序…

无论如何,我对我的脚本感到非常满意,因为它可以在不到一分钟的时间内在 RAM 和 Flash 中找到密钥(在 Flash 中要少得多):

$   python3   cm-decrypt.py   validation   -tests/file_modem.bin   -b   validation   -tests/ram_dump/ram_b_PackToEncryptionLayer.img **   CM   DECRYPT   v1.0   ** [ * ]   Opening   validation   -tests/file_modem.bin...   --   FILE   INFORMATION   -- File   size:   2514   bytes   ( hex:   9D2 ) File   created:   Sat   May   18   00:42:41   2019 File   modified:   Sat   May   18   00:42:41   2019 File   entropy:   7.91   bits   per   byte [ * ]   Data   sanitized! [ * ]   Brute   force   mode [ * ]   Binary:   validation   -tests/ram_dump/ram_b_PackToEncryptionLayer.img   **   Transport   Layer   ** Type   of   packets:   5   ( hex:   05 )   Length:   2478   bytes   ( hex:   09ae ) Unknown:   2   ( hex:   02 ) Packet   ID:   0   ( hex:   0000 ) CM   ID:   [ REDACTED ]   ( hex:   [ REDACTED ] )   Checksum:   47070   ( hex:   b7de ) [ * ]   Brute   forcing   the   AES   key   ... [ * ]   2097152   keys   to   try   ..............................................................   ..............................................................   .................. [ * ]   Key   Found   in   37.69s! Key:   [ REDACTED ] Addr:   0x0015d180

处理完解压层后,我们便得到了消息层。但是,该层主要包含日志消息(指示 HMU 版本和各种网络连接错误)。因为没有起搏器连接到 HMU,所以这是意料之中的。但是,该层也将包含患者数据。

$   python3   cm-decrypt.py   -k   [ REDACTED ]   validation   -tests/   file_modem.bin **   CM   DECRYPT   v1.0   ** [ * ]   Opening   validation   -tests/file_modem.bin... --   FILE   INFORMATION   -- File   size:   2514   bytes   ( hex:   9D2 ) File   created:   Sat   May   18   00:42:41   2019 File   modified:   Sat   May   18   00:42:41   2019 File   entropy:   7.91   bits   per   byte [ * ]   Data   sanitized!   [ * ]   Decrypt   mode **   Transport   Layer   ** Type   of   packets:   5   ( hex:   05 )   Length:   2478   bytes   ( hex:   09ae )   Unknown:   2   ( hex:   02 ) Packet   ID:   0   ( hex:   0000 ) CM   ID:   [ REDACTED ]   ( hex:   [ REDACTED ] )   Checksum:   47070   ( hex:   b7de ) **   Encryption   Layer   ** Length   of   the   packet:   2466   ( hex:   9a2 )   ( div   by   16?   No )   Type   of   packet:   8   ( hex:   08 )   =>  AES_CBC Padding:   15   ( hex:   0f ) IV:   5c8aff9dd3a7bb54f04915bc18e96fbb Key:   [ REDACTED ] **   Compression   Layer   ** Compression   packet:   Yes Magic   header:   0x1f8b   =>  gzip   compressed   data,   from   Unix   Entropy:   7.72 **   Message   Layer   ** Size   of   the   recovered   data:   28538   bytes   Number   of   packets:   105 Entropy:   3.94

所有这些操作都是在 T-Line 版本上执行的,还有一件事需要尝试:在 GSM 版本上是否使用相同的密钥?因为我们也有一些来自 GSM 版本的数据,所以我尝试使用从 T-Line 版本获得的密钥解密它,但是没有成功,这是一件好事!但是,现在知道密钥已存储在闪存中,我们尝试转储 GSM 版本的固件。如果没有合适的连接器,我们只能使用由两个人将跳线保持在引脚上的方式,而第三个人则要启动 OpenOCD" 脚本 "。几秒钟后,由于难以保持静止,操作崩溃了,我们未能获得完整的固件(我们购买了更好的设备后,我们就获得了固件)。

短信数据

如前一篇文章所述,通过窃听微控制器和调制解调器之间的通信线路,我们不仅可以提取通过 GPRS 发送的数据,还可以提取 SMS 数据。仅从 SMS 中获取有效负载,我们剩下以下原始数据(例如):

0604cac0441230acf7b8ef24287fa3954ca200afbbdc44c170dbca0f125cda043f298b476f6377c4f22f2 5ff99e976f9b066ca00aa5a25ab8a47218d21f232ed41aed4e0f22f42e6189968d7cf6b965b73a768d7cf 6b965b73a768d7cf6b965b73a7f6beb75a443cfcfe

无需重新进行整个分析过程。在这里,我们似乎正面临采用 DES 加密的加密层(由开头的 " 06" 表示)。我认为我可以简单地使爆破脚本进行 DES 加密而不是 AES CBC。我尝试了一下,但是在尝试了所有键之后都没有结果。那可能意味着两件事:

· DES 密钥不在我们获得的部分转储中。

· 加密的数据未压缩,因此不是从魔术头 0x091F8B 开始的。

考虑到我有 AES 密钥,我认为第二种选择更有可能,并且还假定我可以使用解密数据的熵而不是魔术头确定密钥。这样做的缺点是必须尝试所有密钥,因此需要更长的时间才能完成。但是,按照上面的解释,这并不是真正的问题,尤其是在部分转储的情况下。因此,该想法是尝试所有可能的密钥,计算解密数据的熵,并始终保持最低的熵(最终约为 4)。

这种方法的效果出奇的好:脚本找到了 DES 密钥,并且我能够解密数据。同样,它是日志消息,主要包含应用程序版本以及错误。使用 DES 加密这些数据的安全性更多,因为它不需要物理访问设备。但是,我后来在与制造商讨论时获悉,SMS 不用于发送患者数据,仅用于发送 HMU 日志。

3. 结论

该项目的这一部分可能是我最喜欢的部分:我一直对密码学感兴趣,并且有能力在 CTF 之外的其他地方面使用它,这非常令人兴奋。在此过程中,我也学到了很多东西,特别是在逆向方面。

现在,你可能想知道,对于以前的帖子中显示的不同发现,该怎么办?在下面的部分中,我们将看到如何使用获得的信息在 HMU 和后端服务器之间发起中间人攻击。

0x03 攻击面分析总结

在前面的部分中,我们分析了家庭监视单元的不同范围,并通过利用相互身份验证和硬编码凭据的不足来设法从设备中提取和解密数据。在这部分中,我们将看到如何将当前的漏洞形成攻击链,以修改起搏器发送到后端服务器的数据。

重要的是要注意,本文中描述的攻击在技术上是可行的,但并不实际。这意味着即使可以进行攻击,也不能将其用于伤害患者或危及患者安全。我将在帖子结尾处回到这一点。此外,提出的攻击仅在 CardioMessenger II 上可行,即使它仍在使用,它也不是制造商的 HMU 的最新版本,也不再在市场上出售。

1. 攻击思路

攻击者的想法是修改起搏器发送到后端服务器的数据,而不触发任何警报 / 警告系统。以不触发任何警报的方式修改数据本身就是一个挑战,而且由于我无法使用正在工作的起搏器,因此我只能假设具有生物医学知识的人可以弄清楚该如何做。为了能够修改这些数据,有几种方法具有不同的要求。图 1 以最简单的方式总结了攻击的概念:攻击者的目标是将布尔值从 " true" 修改为 " false"。

图 1:攻击者的目标

2. 方法 1:通过 JTAG 访问

攻击者可以用来实现其目标的第一种方法是利用对设备的 JTAG 访问,从而控制内存,以便在数据打包和加密之前更改数据。如果我们考虑最简单的情况,即攻击者只想对数据进行一点翻转,那么她将在数据打包之前设置一个断点,对其进行修改,然后继续执行,这样就可以了。

显然,这种方法要求攻击者在整个攻击过程中都可以物理访问设备(更具体地说是 JTAG 引脚),因此不太实用。

一个可能的解决方案是将 Raspberry Pi 设置同时插入为 JTAG 连接器和 HMU 内的访问点,从而提供 " 远程物理访问 "(一个人可以连接到 RPi,然后通过 JTAG 与 MCU 交互)。

3. 方法 2:通过网络访问

第二种方法还要求攻击者至少对 HMU 进行一次物理访问。攻击者的想法是转储足够的内存以获取设备的密钥。一旦攻击者拥有了这些材料,就可以在 HMU 和后端服务器之间的任何位置进行攻击,只要攻击者可以拦截流量即可。这里的一个典型示例可能是移动运营商的内部攻击者。

这次,攻击者可以截获通信,使用先前收集的密钥和与上面相同的脚本对其进行解密,修改数据,再次加密和打包并将其转发给后端服务器。

4. 方法 3:欺骗 HMU

最后,攻击者可能想要完全欺骗 HMU。这也需要对设备进行物理访问,以收集凭据和密钥。除此之外,攻击者还需要访问 VPN,VPN 需要有效的 SIM 卡或电话线(我不确定该设备的版本是否仍在使用中)。这样,攻击者可以连接到 VPN,然后再连接到后端服务。然后,她可以使用 HMU 的密钥将数据发送到服务器以加密消息。这里的困难是整个消息都需要伪造,如果攻击者一开始不知道其外观,这可能是一个挑战。因此,这将需要一些 " 监视 " 时间来收集有效消息,以便以后创建看上去有效的假消息。

5. 攻击实用性

现在,让我们回顾一下为什么这种攻击不切实际。第一个也是最好的论据是,这种攻击只会使家庭监护无用,即就像根本没有家庭监护,只是让患者和医生相信那样。为了使攻击有针对性地伤害患者(将一个特定的人作为目标),攻击者将需要进行很长时间的攻击,并且依靠 " 运气 " 使患者的病情迅速恶化。足以使常规医生的约会无法检测到。

第二点是,攻击需要对设备进行物理访问,对发送到后端的数据有充分的了解,才能在不触发警报的情况下欺骗它并长时间进行攻击以产生潜在影响。从安全研究的角度来看,这增加了攻击的难度和有效性,从而降低了有人甚至尝试将其分开的可能性。

的确,即使这种攻击不能对患者造成任何伤害,它也突出显示了能够信任来自医疗设备的数据的需要。这不是一个容易的问题,当面对起搏器之类的低功率设备时,问题甚至更少。

0x04 研究总结

如引言中所述,此处的想法是介绍可以使用以前的帖子中发现并介绍的各种漏洞来完成利用。更具体地说,目标是表明可以单独链接影响多个漏洞来进行攻击。在与制造商讨论后,我们决定公开我们的发现,明确表明不能将其用于伤害任何患者,这当然是当务之急。

对我来说,从事这个项目是一次很好的学习经历,希望这个博客系列能对我有所帮助。如前所述,我是硬件和嵌入式安全性的入门者(现在仍然是),因此几乎所有工作对我来说都是新的。此外,我有机会与 CISA 和 BSI 一起参与了协调披露流程,这对我也是第一次。我很高兴 BIOTRONIK 认真对待我们的发现并接受讨论:他们为我们提供了对我们报告的详细答复,其中他们解释了为减轻所报告问题而做出的改进。" 起搏器黑客计划 " 的目标是为起搏器生态系统的安全做出贡献,从这个意义上说,这是成功的。

相关文章
评论
没有更多评论了
取消

登录后才可以发布评论哦

打开小程序可以发布评论哦

12 我来说两句…
打开 ZAKER 参与讨论