设计缺陷将导致亚马逊Echo变身成为监听设备
MWR的安全研究专家发现亚马逊Echo存在一个物理攻击漏洞,该漏洞将允许攻击者获得设备的root shell(设备底层为Linux操作系统),然后安装恶意软件,并且不会留下任何攻击痕迹。这种恶意软件不仅可以帮助攻击者获取到目标设备的永久远程访问权并窃取用户凭证,而且还可以悄悄将设备麦克风所记录下的音频流数据发送到攻击者所控制的远程服务器。
这个漏洞是由于以下两个硬件设计缺陷所导致的:
1. 暴露了设备的调试面板;
2. 硬件配置允许设备从外部SD卡引导启动;
接下来,我们会告诉大家如何对亚马逊Echo进行root,并将其变成一台“监听器”。
前人的工作
之前已经有安全人员通过在设备调试面板上插入外部SD卡来将设备引导启动进通用的Linux环境中了,关于具体的操作步骤、漏洞细节和SD卡引导镜像都可以在GitHub上找到。除此之外,研究人员还专门发布了相关的研究报告并介绍了root亚马逊Echo的可能性。
我们在这些研究的基础上进行了进一步的深入扩展,并将亚马逊Echo引导启动进入了真实的系统固件,安装持久化后门、获得了远程root shell的访问权,并最终实现了对设备麦克风的远程实时窃听。
Root设备
将亚马逊Echo底部的橡胶垫揭开之后,我们会看到18个调试面板,关于这些面板的具体作用请参考Clinton等人发表的文章。
通过与这些暴露在外的UART面板相连接,我们就可以查看到设备的启动信息,并获取到设备的配置详情。
但不幸的是,在设备的启动过程中我们既没有拿到shell,也没有得到登录提示,而且U-Boot启动过程也没办法中断。
亚马逊Echo的主MCU是一块由美国德州仪器制造的DM3725数字媒体处理器(配备了一个ARM Cortex-A CPU)。在设备的启动过程中,芯片的启动过程分为三步。首先,设备会通过一个bootrom来完成硬件的最小化配置。接下来,它会将第二个bootloader(X-loader)加载进MCU的内部RAM。这一步需要在第三个bootloader(U-Boot)加载进外部RAM之前完成。最后,U-Boot便会加载内核并向其传递控制信息。
根据亚马逊Echo的配置,它首先会尝试从连接至调试面板的SD卡(优先与内部eMMC单元)启动,这种启动顺序的设置主要取决于启动过程中MCU的硬件状态,而且只能通过修改硬件主板的配置来改变启动顺序。
因此,我们只需要向SD卡写入X-lodaer以及U-Boot,并进行正确的分区,我们就可以让设备从SD卡启动并进入U-Boot命令行界面。
由于设备的隐藏ROM是以SPI模式来与SD卡通信的,而且我们无法通过SD卡启动至设备的主操作系统,所以我们不需要连接上图中所有的SDMMC面板。
MMC与SPI的映射关系如下:
SDMMC D0 → MISO SDMMC D3 → !SS SDMMC CMD → MOSI SDMMC CLOCK → SCK
我们还需要给SDMCC POWER面板和SD卡外接一个+3V电源,然后连接其中一个GND面板。
下图就是我们的实验环境,其中亚马逊Echo连接到了一个外部SD卡电路板,并通过UART与笔记本电脑相连。
接下来,我们通过SD卡(写入了X-loader和U-Boot)启动设备之后,我们就可以中断设备的启动过程,并进入U-Boot命令行接口。这样一来,我们就有可能查看到文件系统内部内存中的内容了,而且我们甚至还可以重新修改内核参数。
现在我们需要确定内部eMMC的哪一个分区中包含主内核以及文件系统。内部eMMC有八个分区,分区标签如下:
1. xloader 2. recovery 3. boot 4. idme 5. diag 6. main-A 7. main-B 8. data
diag分区加载的是一个诊断环境,我们还没有对其进行完整的测试。
我们所要找的主文件系统以及内核会在main-A和main-B分区之间转换,每一次设备固件更新便会引发分区转换。为了找出我们的目标分区,我们可以利用U-Boot并使用下列命令对文件系统进行测试:
uboot> mmc dev 1 uboot> ext4ls mmc 1:6 uboot> ext4ls mmc 1:7
运行上面的命令之后,我们只能够看到其中一个分区的文件系统,如果我们想要查看这两个分区的文件系统,我们就要通过一次固件更新来完成分区转换。确定了分区之后,我们就可以修改U-Boot并让它从这个分区启动。除此之外,我们还要修改内核参数并以可写文件系统的形式加载这个分区,然后运行/bin/sh,而不是运行正常的启动脚本。
得到了root shell之后,我们就可以绕过所有的认证机制了。
但是现在设备每隔几分钟就会重启一次,为了防止设备自动重启,我们需要设置一个监控进程,并定期重置设备的重启计时器。
运行下列命令启动监控进程(watchdog):
现在测试环境总算是稳定了,但是主要的服务都还没有启动,设备的功能也没有完全释放。不过我们已经得到了整个文件系统的完整读写权限,而且还可以随意修改配置。
在我们的PoC中,我们在data分区(可写分区)中安装了一个反向shell脚本。使用下列命令加载这个分区:
分区加载完成之后,我们就可以通过这个反向shell脚本来实现设备的持久化感染了。
我们还需要在设备启动之后触发这个反向shell脚本,我们只需要在其中一个初始化脚本的结尾添加下列代码即可。我们选择的是/etc/init.d/varlocal.sh,因为它是最后几个运行的初始化脚本之一,而且它还可以加载data分区。
安装好反向shell脚本之后,我们就可以移除外部SD卡和UART连接线,然后重启Echo并进入正常状态了。在设备重启的过程中,初始化脚本会运行我们的反向shell。如果我们远程监听设备的1377端口,我们就可以通过root shell与远程设备进行连接了:
你有在听吗?
接下来,我们就可以使用亚马逊自己开发的“shmbuf_tool”应用程序来获取音频缓冲区中的音频多媒体数据了。我们自己创建了一个能够持续不断地向fifo管道写入原始音频数据(通过麦克风窃听)的脚本,并通过TCP/IP将音频流传送到远程服务器。在远程服务器端接收到原始音频数据之后,我们可以将其保存为一个wav文件或直接通过扬声器进行播放。
需要注意的是,这项攻击技术并不会影响亚马逊Echo原本的正常功能。
脚本代码startStream.sh如下:
下列命令可以将音频流数据保存到远程服务端:
或者使用下列命令通过扬声器播放窃听数据:
漏洞修复
2015和2016版的亚马逊Echo均存在这个漏洞,但是2017版已经修复了这个问题。
下图为存在漏洞的2016版亚马逊Echo,型号编码23-002518-01:
下图为修复后的2017版亚马逊Echo,型号编码23-002518-02:
参考资料
1. https://github.com/echohacking/wiki/wiki/Echo 2. https://vanderpot.com/Clinton_Cook_Paper.pdf 3.https://www.theverge.com/circuitbreaker/2016/12/14/13955878/wynn-las-vegas-amazon-echo-hotel-room-privacy
- hiveQL求差集
- hiveQL去重
- springboot高并发redis细粒度加锁(key粒度加锁)
- java使用spark/spark-sql处理schema数据
- redis的发布订阅模式pubsub
- linux命令和awk
- django集成celery之callback方式link_error和on_failure
- 使用beanstalkd实现定制化持续集成过程中pipeline
- 用SQLite查看编辑android导出的微信聊天记录
- 使用HDFS客户端java api读取hadoop集群上的信息
- 使用Fabric一键批量部署上线/线上环境监控
- springboot使用zookeeper(curator)实现注册发现与负载均衡
- django使用xlwt导出excel文件
- redis的sentinel主从切换(failover)与Jedis线程池自动重连
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 从0开始做播放器-第二季-第2章-Android NDK 工程的建立和 JNI 的基本用法
- 记一次线上问题排查-maven父子结构依赖所遇到的坑
- 『技术随手学』解决 pip conda install 网络故障中断
- boost asio
- 7.SwrContext音频重采样使用
- 8.ffmpeg-基础常用知识
- 9.下载ffmpeg、使QT支持同时编译32位和64位
- 10.QT-QAudioOutput类使用
- 11.QT-ffmpeg+QAudioOutput实现音频播放器
- Spring JPA 自定义删改
- LeetCode-28.实现 strStr()
- 【工具篇】程序员不愿意写 PPT 是姿势不对?
- 机器学习还能预测心血管疾病?没错,我用Python写出来了
- Fasttext 总结
- PathClassLoader加载与查找类