如何“用 QEMU 模拟它”

https://www.zerodayinitiative.com/blog/2020/5/27/mindshare-how-to-just-emulate-it-with-qemu

通常,人们认为路由器或物联网安全研究很容易。“只需用 QEMU 模拟它!” 通常就是他们所说的。他们可能还会告诉你“低买高卖”在股市中赚钱。交易股票的困难(如果不是不可能的话)部分是知道价格何时处于最低和最高。同样,对于嵌入式安全研究领域的新手来说,知道如何使用 QEMU 模拟固件可能是最难的部分。虽然我无法在交易建议方面提供太多帮助,但我可以在固件仿真方面提供帮助。

在开始之前,我将假设您的固件是类 UNIX 的,并且不在其他一些实时操作系统 (RTOS) 上运行,例如 VxWorks 或 Windows CE。我还将假设您的固件已解密/去混淆并提取了根文件系统。如果您在使用加密固件时遇到问题,请查看我之前关于处理加密固件的博客文章。

开始就要考虑如何结束

设备仿真的一个关键部分是有一个最终目标。你想只运行一个二进制文件吗?只有一项服务怎么样?或者您想要完整的设备模拟?由于使固件仿真正常工作是一项耗时的工作,您的目标将极大地影响您的仿真策略。有时,花费在调整仿真上的无数个小时将证明购买其中一些低成本设备是合理的。

要运行单个二进制文件(例如解密例程),请考虑使用更轻量级的用户空间 QEMU 仿真方法。如果您的目标是编写漏洞利用程序,那么使用物理设备可能是最好的选择,因为漏洞利用程序最终是针对现实世界的设备使用的。仿真可能不考虑细微的硬件行为,例如指令缓存,这可能会影响内存损坏漏洞利用。但是,模拟非常适合开发和测试更高级别漏洞的利用,例如 CGI 脚本命令注入或 PHP 页面中的登录逻辑缺陷。

确定 CPU 架构和信息收集

模拟任何事物的第一步是确定我们目标的 CPU 架构。通常,我们可以在没有手头的设备的情况下确定这一点。确定 CPU 类型的一种方法是分析固件二进制文件。file在任何二进制文件上运行命令可以快速告诉我们我们正在处理的 CPU 架构:

图 1 – file 和 readelf 命令的输出

图 1 – file 和 readelf 命令的输出

但是,该file命令不提供最详细的结果。readelf带有-AARM 二进制选项的命令提供了更详细的 CPU 信息,这对于完整的系统仿真和交叉编译至关重要。

使用无线路由器时确定 CPU 架构的另一种方法(以 TP-Link TL-WR841-ND 为例 [1])是通过在 Internet 上搜索设备型号。这通常会让我们进入一个OpenWRT 设备页面,该页面提供有关设备硬件的信息。快速搜索还可以告诉我们主要的片上系统 (SoC) 部件号以及设备 FCC ID。然后我们可以查找相应 SoC 芯片的数据表并确定确切的 CPU 架构。

这也是搜索特定系列处理器内核数据表以熟悉 CPU 的好时机。该数据表将提供特定于器件的信息,例如加载地址和低级存储器布局,这些信息可能有助于仿真和分析。您还可以查找 FCC 备案报告以了解 设备的内部视图

用户模式仿真

当只需要模拟一个二进制文件时,每进程模拟很有用。有两种方法可以在用户模式 ​​QEMU 中模拟单个二进制文件。第一个选项是用户模式进程仿真。这可以通过以下命令之一完成:

qemu-mipsel -L <prefix> <binary> qemu-arm -L <prefix> <binary> qemu-<arch> -L <prefix> <binary>

-L当二进制链接到外部依赖项(例如uCLibc或加密库)时,该选项很重要。它告诉动态链接器使用提供的前缀查找依赖项。以下是imgdecrypt为 D-Link DIR-882 路由器运行二进制文件的示例。图 2 - imgdecrypt

图 2 - imgdecrypt

模拟该过程的另一种方法是使用 QEMU 执行跨架构的 chroot。为此,我们将qemu-<arch>-static二进制文件复制到/usr/bin/固件根文件系统的目录中。然后我们chroot进入固件根目录并获得一个工作外壳:

图 3 - 使用 QEMU 执行跨架构的 chroot

图 3 - 使用 QEMU 执行跨架构的 chroot

这是可能的,因为 QEMU 在安装期间向内核注册以通过该binfmt_misc机制处理具有某些魔术字节的二进制文件。因此,该技术利用相同机制的 Scratchbox交叉编译工具包不兼容。您可以在这篇StackOverflow 帖子中找到对跨架构 chroot 的更详细说明。

这种方法是我首选的模拟设备的第一次尝试。它可以快速设置并允许我在固件根文件系统中试验不同的二进制文件,而不必过多担心依赖性。请注意,在这种模拟模式下,chroot shell 中没有初始化用户态服务,因此没有任何系统或网络服务可用。但是,这对于仅运行一个二进制文件或测试一个小组件就足够了。

带出大炮:全系统仿真

有时,我们需要更全面地分析固件,并将受益于完整的系统仿真。有很多方法可以完全模拟设备。以下是一些最常见的仿真技术。研究人员已使用这些技术来查找随后提交给 ZDI 程序的真正错误。

在仿真过程的第一部分,我们将使用 QEMU 创建一个在目标架构上运行的完整 Linux 虚拟机。然后我们将固件根文件系统传输到 VM 中,将 chroot 传输到固件的根文件系统中。要创建在 QEMU 中运行的完整 VM,我们通常需要以下内容:

-- QEMU 磁盘映像文件 (qcow2) -- 为目标架构编译的 Linux 内核映像 --(有时)初始 RAM 磁盘映像 (initrd)

要获得上述项目,您当然可以设置交叉编译器,构建内核,并下载安装程序以获取初始 RAM 磁盘。然后您可以将 Linux 安装到 QEMU 磁盘映像文件中。然而,交叉编译内核对于休闲的 bug 猎人或 Linux 初学者来说是一项重要的附带任务。如果您有兴趣自己准备这些文件,请查看进一步阅读部分中的链接。在这篇博客中,我们将采用一种更简单的方法。我们将下载并使用由 Debian 开发人员 Aurelien Jarn 准备的预构建 Debian 映像。或者,您可以使用“gef”插件的作者Chris ( @_hugsy_ )提供的图像

准备好所有文件后,我们可以使用以下命令之一启动具有适当 CPU 架构的 QEMU VM:

sudo qemu-system-mipsel -M malta -kernel vmlinux-2.6.32-5-4kc-malta -hda debian_squeeze_mipsel_standard.qcow2 -append "root=/dev/sda1 console=tty0"

sudo qemu-system-mipsel -M malta -kernel vmlinux-3.2.0-4-4kc-malta -hda debian_wheezy_mipsel_standard.qcow2 -append "root=/dev/sda1 console=tty0"

sudo qemu-system-arm -M vexpress-a9 -kernel vmlinuz-3.2.0-4-vexpress -initrd initrd.img-3.2.0-4-vexpress -drive if=sd,file=debian_wheezy_armhf_standard.qcow2 -append "root=/dev/mmcblk0p2"

sudo qemu-system-arm -M vexpress-a9 -kernel vmlinuz-3.2.0-4-vexpress -initrd initrd.img-3.2.0-4-vexpress -drive if=sd,file=debian_wheezy_armhf_desktop.qcow2 -append "root=/dev/mmcblk0p2"

view rawmindshare_20200527.console hosted with ❤ by GitHub

-M,或-machine选项,指定董事会模型QEMU支持,该选项允许用户选择目标硬件平台。这些-append选项允许您调整传递到 Linux 内核的内核选项。我喜欢将 QEMU 命令放入 bash 脚本中,以加快调整和启动 VM 的过程。此外,我们应该将以下选项附加到 QEMU 调用以连接网络接口并添加端口转发设置:

-net user,hostfwd=tcp::80-:80,hostfwd=tcp::443-:443,hostfwd=tcp::2222-:22 \ -net nic

添加这些选项将允许我们通过主机的 2222 端口以及模拟固件的 HTTP 和 HTTPS 页面通过 SSH 与 VM 进行通信。

图 4 - 启动预先构建的 Debian 映像

图 4 - 启动预先构建的 Debian 映像

一旦 VM 启动并为我们提供了一个可用的 Debian VM,仿真的第二部分就开始了。使用 SCP 或 HTTP 将固件的根文件系统传输到 VM。我发现将整个根文件系统打包在一个 TAR 球中是处理传输的最有效方法。然后,我们需要安装/proc/dev以及/sys虚拟机的目录中的固件文件系统中的相应文件。最后,我们chroot使用以下命令进入固件文件系统:

chroot ~/firmware_rootfs /bin/sh

第二个选项告诉在更改根目录后chroot运行/bin/sh。您可能需要将此命令更改为/bin/bash/bin/busybox获取工作 shell。

图 5 - Busybox

图 5 - Busybox

使用可工作的 shell,我们可以导航到/etc/rc.d/etc/init.d并运行适当的 RC 脚本以启动用户空间服务。仔细分析rc.d文件夹并检查脚本,您需要调整启动脚本以解决丢失的网络接口、NVRAM 库调用失败以及各种有趣的问题。这部分仿真过程很像处理加密固件;每个固件都将是一次自己的冒险,这正是研究的定义。通常,您需要对rcS脚本进行足以使目标服务正常运行的调整。这部分过程可能需要数周的调查和额外的工作。

仿真工作量很大。有时站在巨人的肩膀上是更好的选择。有两个主要项目可以帮助加快固件仿真过程,即 Firmadyne 和 ARM-X。

60% 的时间,它每次都有效:Firmadyne

Firmadyne在工作时很棒。它是一个固件仿真平台,尝试自动仿真基于 Linux 的设备固件。Firmadyne 支持 MIPS 和 ARM 处理器。它将提取根文件系统,推断网络接口,并创建用于仿真的 QEMU 磁盘映像。它还尝试模拟 NVRAM。如果您需要对新目标进行完整的系统仿真,我建议您先尝试 Firmadyne。然后,您可以尝试修复它遇到的一些错误,然后再尝试其他仿真技术。我在使用较新的 QEMU 版本运行 Firmadyne 时遇到了问题。但是,使用 Docker 安装它通常可以避免这个问题。

ARM-X

ARM-X固件仿真框架目标的基于ARM的固件。它是使用 QEMU 模拟固件的内核、脚本和文件系统的集合。它附带了一些仿真配置示例来帮助您完成项目。我建议在试用 ARM-X VM 之前,先在YouTube观看Saumil Shah ( @therealsaumil )录制的长达一小时的 Hack In The Box 2019 演示文稿。如果您对 IoT 固件研究完全陌生,那么该演示文稿也是一个很好的入门资源。

结论

希望在此博客的帮助下,您已准备好“使用 QEMU 模拟它”。上面展示的所有技术(包括 ARM-X 和 Firmadyne)都用于我们程序的各种提交。条条大路通罗马,但没有一种单一的、固定的方法可以用 QEMU 模拟固件。探索不同的技术,看看什么适合你。熟悉使用名为 QEMU 的野兽的知识,您会惊讶于它如何以意想不到的方式帮助您。最后,我很想了解您的仿真技术,并期待您的下一次提交。

你可以在 Twitter @TrendyTofu上找到我,并关注团队获取最新的漏洞利用技术和安全补丁。

进一步阅读

MIPSEL QEMU 镜像准备 https://blahcat.github.io/2017/07/14/building-a-debian-stretch-qemu-image-for-mipsel/

仿真 ARM 机器上的 Debian https://www.aurel32.net/info/debian_arm_qemu.php

Debian 在模拟 MIPS(EL) 机器上 https://www.aurel32.net/info/debian_mips_qemu.php

Arm1176 (ARMv6) QEMU 仿真 https://azeria-labs.com/emulate-raspberry-pi-with-qemu/

AArch64 (ARMv8) QEMU 镜像准备 https://blahcat.github.io/2018/01/07/building-a-debian-stretch-qemu-image-for-aarch64/

ARM 仿真 https://balau82.wordpress.com/arm-emulation/

脚注

[1] 这款极其便宜的路由器是您开启漏洞研究之旅的绝佳目标。看看我的2 -部分系列开始使用此设备

Last updated