在过去的几个月中,我一直在培训人们利用嵌入式设备进行攻击。 我的幻灯片无法提供足够的信息,所以我想把所有东西都写出来供人们在线消化。 以下博文是“第1部分”,它将向读者介绍嵌入式设备的软件方面。 我决定首先涵盖软件,因为大部分漏洞都存在于软件堆栈中,不论二进制应用程序还是驱动程序。 第2部分将介绍硬件堆栈,重点介绍JTAG是如何实际工作的以及如何利用修改硬件来绕过密码保护或提取烧入目标器件的信息。
一旦你能够获得嵌入设备中的固件文件,你就会想看看里面有什么。 幸运的是有一个名为Binwalk的开源工具,它可以解析目标二进制文件中可以找到的魔术字节。
为了给出这一切的可视化表示,我将使用Binwalk提取DVRFv0.3。
使用binwalk -e file_name 来提取镜像的内容
binwalk检测到的结构以及在其中的的偏移量
在vim中使用xxd显示由binwalk为TRX和gzip提供的偏移量匹配。 (使用vim打开DVRF_v03.bin,然后输入:%!xxd)
交叉引用binwalk使用的TRX结构。
在vim中使用xxd来交叉引用binwalk检测到的SquashFS结构的偏移量。
如果您对目标设备的ASM没有经验,可以使用C和反汇编器快速学习它。 在我看来,以下是学习新的ASM时首先要看的最重要的事情:
参数传递
函数入口和返回
堆栈使用
函数调用
分支条件
在这篇文章中,我将展示我是如何学习MIPS-I ASM的,只是使用反汇编和C语言。
这是一个非常简单的C应用程序,它将两个(int)参数传递给另一个函数返回两个整数之和。
一旦交叉编译完你的C应用程序,将它扔进一个反汇编器。使用任何你熟悉的反编译器,这里我使用radare2
当我们查看图形视图时,我们可以按g然后a再查看pass_args_to_me函数。
确保考虑传递给函数的参数数量大于可用参数寄存器数量的情况。 例如MIPS利用$ a0 - $ a3,让我们修改我们上面写的代码,使其具有4个以上的参数。
就像以前一样,我们将使用交叉编译器进行编译,然后放入Radare2来查看编译器生成的内容
所以现在我们知道如果有超过4个参数被传递给一个函数,那么多出的参数将被推入栈中。
在MIPS和基于ARM的处理器上需要注意的一件事是专用返回地址寄存器。 无论何时在MIPS上执行“跳转和链接”指令,都要知道该寄存器的地址将是指令指针的当前地址加上8个字节。 8字节的偏移量是由于流水线操作引起的,因为PC + 4将在跳转发生之前执行。 让我们编译一个在最终返回main()之前调用2个或更多函数的应用程序。
所以请记住,一个调用(JAL)会用$ PC + 8填充返回地址寄存器,但如果被调用函数调用另一个函数,$ ra寄存器将被覆盖,并且被调用者的地址将会丢失。 为防止这种情况,函数输入时首先将返回地址保存到堆栈中。 有了这些知识,我们将看到除了call_two之外,所有函数都会将返回地址保存到堆栈中,因为该函数不会调用任何其他函数。
通过分析函数入口,我们可以预测函数是否会调用另一个函数。 这在尝试查找位于堆栈中的内存破坏漏洞时非常有用。
研究人员在分析新体系结构时需要了解的最重要的一件事是处理器如何处理分支。 就像以前一样,我们将利用C和Radare2来分析这一点。
下面的应用程序将从argv [1]中获取输入,并将其作为int进行转换,并查看它是否小于5。
现在我们来看看编译器会产生什么来满足我们的条件。
每当我们做比较时,我们就可以看到slti指令的用法。 由于大量的比较运算符和类型,条件语句将成为学习新汇编语言中最耗时的部分。 请参考C参考文献,以确保您分析了生成条件分支的所有不同方法。 例如在MIPS中,有些条件语句使用可能被滥用的有符号或无符号立即数。
现在你已经看到了一些例子,只要你有一个编译器和反汇编器,你就应该有能力来学习任何处理器的架构和汇编。 如果你不这样做,那么你将不得不以更加艰难的方式去做,阅读处理器的开发者手册,最终设计你自己的汇编器,模拟器和反汇编器。
如果您正在审计使用开源软件的设备,该软件可能会根据通用公共许可证进行许可。 如果是这样,那么简而言之,如果开发人员使用代码并编译它,则必须提供源代码! 如果开发者拒绝开源,那么他们就违反了GPL。
考虑到这一点,许多路由器或其他小型设备都使用Linux(或FreeRTOS),Busybox和其他利用GPL的开源软件。 因此,在开始反汇编之前,尝试使用短语“$供应商源代码”或“$产品源代码”进行简单的Google搜索。 以下是我只是通过Google搜索发现的一些示例源代码库。
Samsung Open Source Release Center
ASUS RT-AC68U Source Code
Belkin Open Source Code Center
GPL Code Center - TP-LINK
现在您应该了解如何轻松利用Google或您最喜爱的搜索引擎来查找您正在审计设备的源代码。
本节将假设读者具有利用基于内存损坏的漏洞的基本知识。 如果没有,请参阅本页底部的SmashtheStack参考。 SmashtheStack是我个人开始进入x86开发之旅的地方。
如果您正在审计基于MIPS的Linux嵌入式设备,那么在分析您的目标二进制文件时,您已经看到以下几点:
正如你所看到的堆栈和堆区域被标记为可执行的,所以NX不用担心。 尽管堆栈是可执行的,但为了获得代码执行,还需要一些ROP操作。 您还会发现ASLR并未在大多数设备上实现,因此我们不必首先找到信息泄露错误。
使用Binwalk提取固件,你就会想要模拟二进制文件来分析崩溃。我个人使用可在解压缩固件的chroot环境中启动的QEMU的静态内置版本。这使exp开发人员能够访问与嵌入式设备使用的相同libc库,因此唯一会改变的是libc地址。有时需要创建一个完整的QEMU系统,因为主机可能不支持程序正在使用的IO调用导致崩溃。
如果您使用的是基于Debian的Linux Distro,那么您可以通过apt-get来安装QEMU。 sudo apt-get install qemu-user-static qemu-system- *
一旦你安装了QEMU,你就需要将二进制文件复制到解压缩固件的根目录下。在这个例子中,我们将使用DVRF_v03的MIPS Little Endian模拟器。 cpwhich qemu-mipsel-static
./
我们将使用pwnable binary / pwnable / Intro / stack_bof_01并为它制作一个小小的漏洞。然后,我们将把我们的有效载荷粘贴到实际设备上,以查看发生了什么。
二进制的源代码如下:
所以我们有一个简单的基于栈的缓冲区溢出漏洞。 目标是执行函数“dat_shell”,但在分析ELF文件时,我们看到以下内容:
Entry point address: 0x00400630
由于我们的载荷中不能有NULL字节,我们将不得不依赖于执行部分覆盖。 由于这是Little Endian,我们可以覆盖3个最低字节,同时将最高字节设置为NULL。 如果架构是Big Endian,这种技术将无法工作。
为了演示模拟你的环境的能力,我将制作有效载荷,同时展示如何获取所有加载库的模拟地址。
然后,我们将GDB本地附加到端口1234
我们看到PC被设置为A8gA,它位于偏移204,它告诉我们我们保存的指令位置是208字节,但我们只会覆盖这4个字节中的3个。
再试一次,使最终目标$RA 0x00424242。
我们希望跳过修改$ gp的指令,因为应用程序会崩溃。 所以我们将跳转到0x0040095c。
我们甚至可以设置一个断点来确保我们跳转到函数内的正确偏移处。
我们采用精确的有效载荷到嵌入式设备,并且漏洞应该按预期工作。
要在gdb中查找导入的库的地址,需要执行以下操作:
要获得基地址,您需要从表格中的“From”地址中减去Entry Point地址。 例如,通过执行以下操作可以找到libc.so.0的基址:
libc.so.0 = 0x766eb710 - 0x6710 = 0x766E5000
然后可以利用Radare2反编译库给出指令的偏移。
因此,一旦建立了ROP链,您只需要替换可通过cat / proc / [pid] / maps找到的libc地址。 基地址就是你需要的。 如果ROP在QEMU工作,它有99%的可能性在实际设备上工作。
在设计DVRF项目中的练习时,我想包括我亲眼所见的最常见类型的漏洞。 最常见的是基于堆栈的缓冲区溢出,如果您不熟悉ASM,这可能有点难度。
下面的漏洞利用代码需要花费大约8个小时才能完成,因为我仍然在学习MIPS汇编,但是这个漏洞利用在QEMU中制作,然后交给Linksys设备。
由于堆栈是可执行的,并且库地址不会移动,我们可以对我们的ROP链进行硬编码,但最终ROP链的想法是将$SP内的值移动到可以调用的寄存器中。 硬编码堆栈地址在我看来并不可靠,我宁愿使用偏移量代替。 下面是pwnable二进制的map输出。
地址0x2ab3e000是libc可执行区域的基址,因此这是在QEMU漏洞利用时在设备上定位的唯一值。
整个ROP链与Radare2的/R功能一起使用。例如,我正在为我的上一个ROP小工具寻找“move t9,a1”,并通过执行以下操作来找到它:
注意:最初我打算编写自己的shellcode,但被通知一个名为Bowcaster的项目,该项目已经有了shellcode。为了展示这个过程,我将置C代码于独立位置。
如果看一下Bowcaster Reverse_TCP shellcode,会看发现上面的C代码和Bowcaster Shellcode是相同的。
我们可以看到值4183的系统调用是对C函数socket()的调用。 所有其他系统调用都以相同的方式进行了验证。
另请注意,shellcode在用户态QEMU中不能正常工作。 你将看到的这个shellcode的是一个带有错误消息的TCP连接而不是交互式shell。 在实际设备上运行时的shellcode按预期执行。
分析运行期间执行的指令的一种简单方法是利用Qira。 下图显示了Qira如何用于分析二进制文件而无需设置断点。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2019-2-1 18:34
被admin编辑
,原因: