0x00 前言
现在的DSLR相机(数码单反)已经不是以前祖辈们使用过的古董摄像机,而是嵌入式数字设备,可以通过USB连接到电脑,并且最新款还支持WiFi功能。虽然USB以及WiFi可以用来将照片从相机导入手机或者电脑上,但也会将相机暴露在周围环境中。
我们的研究结果表明,如果攻击者已经处于近场环境(WiFi模式)或者已经劫持PC端(USB模式),那么就可以传播恶意软件并感染目标相机。想象一下,如果攻击者将勒索软件注入主机以及相机中,除非我们支付赎金才能赎回所有照片,这是多么悲惨的一件事。
大家可以先观看该视频了解攻击过程。
0x00 图片传输协议(PTP)
现代的DSLR相机早就不使用胶卷来捕捉并重现图像,国际影像工业协会(International Imaging Industry Association)制定了一款标准化协议,用来将数码图像从相机传输到用户主机。这种协议即为PTP(Picture Transfer Protocol)。最初该协议主要关注的是图片传输,但现在包含各种不同的命令,可以支持其他场景,比如拍摄实时图像、升级相机固件等。
虽然大多数用户会使用USB线缆将相机连接到电脑,但新款的相机还支持WiFi功能。这意味着之前我们只能通过USB连接设备来使用PTP/USB协议,但现在只要连入WiFi,就可以使用PTP/IP协议来访问支持WiFi的设备。
在之前的一次“Paparazzi over IP”演讲中(HITB 2013),Daniel Mende(ERNW)演示了如何利用佳能EOS相机当时支持的所有网络协议来发起针对性的网络攻击。在演讲快结束时,Daniel讨论了PTP/IP网络协议,表示攻击者可以从网络中嗅探特殊的GUID值(当目标主机与相机完成配对时会生成一个GUID),从而与相机进行通信。由于PTP协议中支持各种命令,并且没有进行身份认证,也没有进行加密,因此Daniel成功演示了如何滥用协议自身的功能来监控受害者。
在此次研究中,我们的目标是进一步利用协议的功能来完成其他任务。我们模拟攻击者角色,想寻找协议实现中存在的漏洞,希望利用这些漏洞来控制相机。挖掘RCE(远程代码执行)漏洞后,攻击者就可以对相机为所欲为,运行勒索软件只是其中的一个选择而已。
从攻击者的角度来看,PTP协议似乎是不错的目标,原因如下:
- PTP是没有部署身份认证机制的一个协议,同时支持各种复杂的命令;
- 我们可以通过USB或者WiFi来利用PTP中的漏洞;
- 支持WiFi功能使得附近的攻击者更容易操控相机。
在本文中,我们主要将PTP作为攻击渠道,介绍了攻击者可能利用的两种途径:
- USB:控制目标PC的攻击者可以选择该路径,将恶意软件传播到目标相机;
- WiFi:攻击者可以在人流较多的位置放置恶意WiFi接入点,感染目标相机。
不论哪种场景,攻击者的目标都是用户相机。如果攻击成功,用户可能需要支付赎金,才能赎回心爱的相机以及相片文件。
0x01 目标设备
我们的目标是佳能的EOS 80D数码单反,选择该设备的原因包括:
- 佳能是最大的DSLR制造商,市场份额已超过50%;
- EOS 80D支持USB以及WiFi功能;
- 佳能拥有非常活跃的“modding”社区:Magic Lantern。
Magic Lantern(ML)是一个开源免费的软件插件,可以将新功能添加到佳能的EOS相机中。因此,ML社区已经研究过部分固件,也公开过其中的某些API。
攻击者其实非常“简单”,他们会努力寻求以最小的成本获得最大的收益。在这种情况下,针对佳能相机的研究会对用户产生最大的影响,也是最简单的一个起点,这里要感谢ML社区贡献的研究文档。
0x02 获取固件
这估计是研究嵌入式设备时最棘手的一个环节。首先我们要检查厂商网站上是否公开了相关固件文件。如我们所料,经过一番Google后,我们找到了一个固件。下载该文件并解压缩后,我们发现该文件似乎经过加密/压缩处理过,如图1所示。
图1. 固件更新文件中的字节分布情况
根据每个字节的分布规律,我们可以推测该固件经过加密或者压缩处理,并且所使用的算法都比较有效。浏览整个文件,我们无法找到有用的特征,这表明其中可能存在用于bootloader的汇编代码。在许多情况下,bootloader没有被压缩,并且包含解密/解压缩文件所需的指令。
我们尝试了几种解压缩工具,包括Binwalk或者7Zip,都没有得到结果,这意味着这是一种专用的压缩(甚至是加密)方案。经过加密的固件文件非常少见,因为这样厂商就需要额外去管理秘钥。
被困住后,我们又求助Google,看是否有人提到这种.FIR
文件。这里我们就能看到社区的力量,因为ML也必须解决这个问题。实际上,在ML维基中我们发现有个页面提到了关于固件更新文件的“更新保护”措施,该措施多年来已经部署到多个版本中。不幸的是,该页面肯定了我们最初的猜测:这个固件经过AES加密。
考虑到ML是个开源方案,我们希望ML能公布这个加密秘钥,帮助我们解密固件。然而事实并非如此,ML不仅特意保护加密秘钥,我们也无法在互联中找到正确的秘钥。似乎我们又走入了死胡同。
接下来就是检查ML是否针对我们的相机型号进行软件适配,这样其中就会包含调试功能,可以帮助我们dump出固件。虽然相关软件尚未发布,但翻阅论坛和维基后,我们的确找到了一个突破口。ML开发了一个Portable ROM Dumper,这是一个自定义固件更新文件,一旦加载成功,就可以将相机内存导出到SD卡中。在ROM导出过程中相机如图2所示:
图2. 在ROM导出过程中的EOS 80D
根据论坛提供的说明,我们成功导出了相机的固件,将其载入反汇编工具中(IDA Pro)。现在我们终于可以开始寻找相机中是否存在漏洞了。
0x03 逆向PTP层
找到PTP层其实非常简单,只需要结合使用两处有用的资源特征即可:
- PTP层基于命令构建,每条命令都有独特的数字操作码(opcode);
- 固件中包含许多指示字符串,可以简化我们的逆向分析过程。
图3. 固件中PTP相关的字符串
从PTP OpenSession
处理函数开始遍历,我们找到了根据opcode注册所有PTP处理函数的主函数。快速检查后,我们确定固件中的字符串与我们网上找到的文档相匹配。
在分析注册函数时,我们意识到PTP层其实是非常有用的一个攻击面。该函数会注册148个不同的处理函数,这表明厂商支持许多命令。在将近150条命令的基础上,我们很有希望能找到影响程度较大的漏洞。
PTP Handler API
每个PTP命令处理函数都实现了相同的代码API,这些API都会使用ptp_context
对象(感谢ML部分公开了这个对象结构)。对ptp_context
的典型用法如图4所示:
图4. 反编译的PTP处理函数,其中用到了ptp_context
对象
可以看到,这个对象中包含一些函数指针,主要用于:
- 查询传入消息的大小
- 接受传入消息
- 处理完消息后送回响应数据
经过研究后,我们发现大多数命令都比较简单,只接受几个数字参数(因为该协议中每条命令最多支持5个参数)。扫描完相机支持的所有命令后,我们将148条命令范围缩小到38条命令,这些命令都可以接受输入缓冲区。从攻击者的角度来看,我们可以完全控制这些输入缓冲区,因此我们开始在这些命令中寻找漏洞。
幸运的是,每条命令的解析代码都使用的是纯C代码,因此分析起来比较简单明了。很快我们就找到了第一个漏洞。
CVE-2019-5994:SendObjectInfo中的缓冲区溢出
PTP命令名:SendObjectInfo
PTP命令操作码:0x100c
在内部实现中,PTP协议将支持的文件及照片当成“对象”来处理,在这条命令中,用户更新指定对象的元数据(metadata)。该函数在解析对象的Unicode文件名时存在一个缓冲区溢出漏洞。存在漏洞的简化版代码如图5所示:
图5. SendObjectInfo
函数中存在漏洞的代码片段
这是全局上下文中的一个缓冲区溢出漏洞。当逆向分析这个context中的不同字段时,我们找到的可能有问题的唯一一个点就是紧跟在数据拷贝之后的Free操作(Free-Where原语)。我们的拷贝操作能将pKeywordsStringUnicode
字段修改为任意值,然后触发某个调用来释放该字段。
这看上去似乎是不错的一个切入点,但我们还要继续寻找有没有更容易利用的漏洞。
CVE-2019-5998:NotifyBtStatus中的缓冲区溢出
PTP命令名:NotifyBtStatus
PTP命令操作码:0x91F9
虽然我们这款相机型号并不支持蓝牙,但固件中依然遗留了一些蓝牙相关命令,并且攻击者也可以使用这些命令。在测试过程中,我们发现了一个经典的基于栈的缓冲区溢出漏洞,如图6所示。
图6. NotifyBtStatus
函数中存在漏洞的代码片段
这个漏洞利用起来非常简单,因此该漏洞也成为我们的首要目标。通常情况下我们会在这个点停止代码审计,但此时我们还没分析的函数已经比较少,因此决定完成所有分析工作。
CVE-2019-5999:BLERequest中的缓冲区溢出
PTP命令名:BLERequest
PTP命令操作码:0x914C
貌似蓝牙命令比其他命令存在的漏洞更多,这表明开发团队在这方面经验可能有所欠缺。这一次我们发现的是基于堆的一个缓冲区溢出漏洞,如图7所示:
图7. BLERequest
函数中存在漏洞的代码
我们找到了3个相似的漏洞:
- 某全局结构的缓冲区溢出
- 栈缓冲区溢出
- 堆缓冲区溢出
前面提到过,我们想尝试利用基于栈的缓冲区溢出漏洞,那个漏洞看上去可能最为简单。
0x04 代码执行
我们使用USB线将相机连接到电脑,之前正常使用的时候我们也是通过USB线再配合佳能的“EOS Utility”软件,因此首先通过USB传输层来利用漏洞也是非常自然的一件事。搜索Python的PTP库时,我们找到了ptpy,虽然这个库并不能直接使用,但也帮我们在环境设置中节省了许多时间。
在开发漏洞利用代码前,我们先编写了一个小型PoC(Proof-of-Concept),用来触发我们发现的每个漏洞,希望最后能让相机崩溃。如图8所示,相机成功崩溃,厂商给出的信息为“Err 70”。
图8. 测试PoC时出现的崩溃信息
现在我们确定找到的所有漏洞的确有效,可以开始研发利用代码了。
目前我们的相机上并没有安装调试器或者ML,相机也没有拆盖,因此也没有基于硬件的调试接口。我们不知道固件的地址空间信息,除了我们在反汇编器中看到的代码地址。目前我们只通过USB连接到相机,想要两眼抓瞎利用基于栈的缓冲区溢出漏洞。不论如何,来试一下吧。
我们的计划是利用Sleep()
函数来作为断点,测试是否可以在指定秒数后导致设备崩溃。这样就能确认我们的确接管了执行流,触发对Sleep()
的调用。虽然这个计划听起来不错,但显然相机有自己的计划。大多数情况下,这种测试方式通常不会导致崩溃,只会让相机挂起。显然我们无法区分挂起和sleep后挂起,因此我们的断点策略也无计可施。
我们的目的是想办法知道相机已执行到我们控制的代码区域,因此我们决定改变策略。我们发现了一处代码地址,当执行到这个位置时总会触发“Err 70”错误。因此,我们会将断点设置成这个地址。出现崩溃则意味着我们触发了断点,而挂起则表明我们没有触发断点。
我们逐步构建利用代码,最终可以执行自己的汇编代码段,达到了代码执行效果。
0x05 加载Scout
Scout是我使用的一个调试器,这是我在FAX研究期间开发的基于指令的一个调试器,在此次研究中也能发挥很好的作用。然而,通常我们会使用Scout的TCP加载器,需要网络连接。虽然我们可以使用文件加载器,从SD卡加载Scout,但后面还是需要建立网络连接,因此我们希望现在就解决这两个问题。
测试相机的不同设置后,我们发现在连接USB时相机不能同时使用WiFi,这很可能是因为PTP层都用到了这两种方式,并且不支持同时使用这两种方式。因此我们决定切换成WiFi模式。
其实WiFi接口也不能直接利用,但最终我们开发了一个Python脚本,可以通过无线方式来发送利用脚本。不幸的是,脚本运行中突然中断。深入研究后,我们猜测这是因为我们从漏洞函数返回前相机已经崩溃,这样就阻止了基于栈的漏洞利用。虽然我们还不知道相机为什么会崩溃,但貌似在连接WiFi的情况下发送关于蓝牙状态的一个通知会让相机很困扰,不知道怎么处理(特别是这款相机并不支持蓝牙模块)。
我们又回到了原点,可以尝试利用其他两个漏洞。然而,其中某个漏洞也是位于蓝牙模块中,看起来并不乐观。因此我们再次检查了PTP命令处理函数列表,这次彻底分析了每个函数。幸运的是,我们又找到了其他漏洞。
CVE-2019-6000:SendHostInfo中的缓冲区溢出
PTP命令名:SendHostInfo
PTP命令操作码:0x91E4
如图9所示,观察漏洞代码,我们就知道为什么第一次我们会忽略这个漏洞。
图9. SendHostInfo
函数中的漏洞代码片段
这一次开发者会检查消息的大小是否为固定的100个字节,然而,他们还是忘记了至关重要的一些点。那就是非法的数据包虽然会被记录下来,但没有被丢弃。在WiFi测试环境中快速检查后,我们的确看到设备出现崩溃。日志记录函数并不是assert
表达式,因此不会停止基于栈的缓冲区溢出漏洞执行流。
虽然这个漏洞的确满足我们的要求,但我们想继续寻找更多目标,因为这种类型的漏洞很有可能会存在于其他命令中。
CVE-2019-6001:SetAdapterBatteryReport中的缓冲区溢出
PTP命令名:SendAdapterBatteryReport
PTP命令操作码:0x91FD
通过这种代码模式我们又找到了另一个漏洞,这也是列表中的最后一个命令。存在漏洞的简化版PTP处理函数如图10所示:
图10. SendAdapterBatteryReport
函数中存在漏洞的代码片段
这一次栈缓冲区非常小,因此我们会继续使用上一个漏洞。
备注:在WiFi环境中测试这个漏洞时,我们发现在函数返回前相机也会崩溃,我们只能通过USB连接来利用这个漏洞。
0x06 再次加载Scout
找到新漏洞后,我们完成漏洞利用,成功在相机上加载Scout。现在我们已经可以使用网络调试器,可以开始dump内存地址,帮助我们逆向分析程序固件。
然而我们还没完成任务。我们的目标是想演示相机也可以使用PTP协议,通过USB和WiFi来劫持。虽然这两个传输层之间存在细微区别,但最终我们找到的漏洞都适用于这两种情况。然而成功接管相机执行流程只是我们演示场景的第一步,现在我们需要创建一些勒索软件了。
0x07 加密
真正的勒索软件都需要加密函数来加密存放在设备上的文件。前面提到过,固件更新过程中涉及到AES加密算法,这听起来似乎是一次性解决所有任务的绝佳机会。
逆向分析过程比我们想象的还要顺利,我们不仅发现了AES函数,还发现了固件更新过程中的验证及解密秘钥。由于AES是一种对称加密算法,同样的秘钥也可以用来加密恶意固件更新文件,然后用来签名,这样就能通过加密校验。
我们选择使用Scout,而不是自己去实现所有复杂的加密算法。我们实现了一条新的指令,可以用来模拟固件更新过程,送回算法计算出的加密特征值。利用这条指令,现在我们澄清了固件更新文件中每部分对应的正确校验值,获得了相机可用的一种签名原语。
由于我们只有一个相机,这是比较棘手的一个问题。我们想要测试自定义的更新固件,但又不想让相机变砖。幸运的是,我们进行的比较顺利,我们通过patch ML的ROM Dumper得到的自定义的Rom Dumper如图11所示:
图11. 自定义ROM Dumper
CVE-2019-5995:恶意固件静默更新
相机中包含一个PTP命令,可以用于远程固件更新,不需要用户交互。这意味着即便我们发现的所有漏洞被修复,攻击者还是可以使用恶意的固件更新文件来感染目标相机。
0x08 收尾
研究完固件更新过程后,我们可以完成勒索软件开发。勒索软件采用与固件更新过程相同的加密函数,会调用固件中的AES函数。加密完SD卡上的所有文件后,勒索软件会向用户展示一个勒索消息。
为了完成整个攻击过程,攻击者首先需要设置恶意的WiFi接入点。为了完成该任务,攻击者首先可以嗅探网络,然后使用相机会自动连接的WiFi接入点名称来伪造AP。只要攻击者与相机同处于一个LAN中,就可以发起攻击。
漏洞利用过程及恶意软件运行效果可参考此处视频。
0x09 时间线
- 2019年3月31日 – 向佳能报告漏洞情况。
- 2019年5月14日 – 佳能验证我们提交的所有漏洞,双方协作努力修补漏洞。
- 2019年7月8日 – 验证佳能提供的补丁的有效性。
- 2019年8月6日 – 佳能通过官方的安全公告发布补丁。
0x0A 官方安全公告
佳能提供的官方安全公告如下所示:
- 日语版:https://global.canon/ja/support/security/d-camera.html
- 英文版:https://global.canon/en/support/security/d-camera.html
强烈建议大家及时修复受影响的相机。
0x0B 总结
在这次研究中,我们发现佳能实现的PTP协议中存在多个漏洞。虽然我们测试的固件中包含许多专用命令,但协议本身是标准化协议,也适用于其他相机中。根据我们的研究结果,我们认为其他厂商对PTP协议的实现中可能存在类似的漏洞。
我们的研究结果表明,任何“智能”设备(这里为DSLR相机)都容易受到攻击影响。考虑到价格、敏感内容以及广泛的消费者受众,这些因素结合起来使得相机也是攻击者热衷的一类目标。
最后再稍微提一下固件加密。利用Magic Lantern的ROM Dumper,再配合固件自身中包含的一些函数,我们成功绕过了目标设备的加密及验证机制。这个例子再次表明,不透明性并不等同于安全性,当攻击者只需要付出少量精力就能绕过这些加密层时,情况就会变得更加糟糕。