本文讲的是
以DVRF(路由器漏洞靶机)为例解读JEB固件漏洞利用,
在本文中,我将介绍JEB的MIPS反编译器是如何帮助你查找和利用嵌入式设备中的软件漏洞。
DVRF
DVRF(Damn_Vulnerable_Router_Firmware) 是一个基于路由器Linksys E1550的路由器固件,里面包含了开发者写的一些存在漏洞的二进制文件,可以在路由器中安装该固件进行安全测试。
DVRF模拟了一个比较真实的环境,比较适合初学路由器漏洞挖掘,不过前提是对其他CPU架构(MIPS)有一个基本的了解。
对于有兴趣挑战的读者,我建议你遵循DVRF教程,并获得完整的MIPSEL Debian QEMU映像,因为该映像可以在Linux上使用常用的漏洞开发利用工作流程,而不会对可用工具产生任何限制。
JEB
JEB是一个功能强大的为安全专业人士设计的Android应用程序的反编译工具。用于逆向工程或审计APK文件,可以提高效率减少许多工程师的分析时间。具有以下4大特点:
1.全面的Dalvik反编译器
JEB的独特功能是,其Dalvik字节码反编译为Java源代码的能力。无需DEX-JAR转换工具。
2.交互性
分析师需要灵活的工具,特别是在处理混淆的或受保护的代码块时。JEB的强大的用户界面,使你可以检查交叉引用,重命名的方法,字段,类,代码和数据之间导航,做笔记,添加注释,以及更多。
3.可全面测试APK文件内容
检查解压缩的资源和资产,证书,字符串和常量等。追踪你的测试进展情况。
不会让以前研究的工作付诸东流,可以保存你的分析,对JEB数据库文件,通过JEB的修订历史记录机制和跟踪进展。
4.多平台
JEB支持Windows,Linux和Mac OS。
漏洞利用环境设置
首先,我从二进制解压缩固件中提取二进制文件。我先对第一个文件做一些分析:
加载到JEB后,我可以看到几个有趣的功能:
在一些经典的libc有趣的例程中(system,strcpy……),我注意到了被命名为“dat_shell”的函数。
正如上图所显示的,此函数可以帮你解决问题,并通过调用系统产生一个shell。现在,我要将执行流重定向到dat_shell函数。接下来,我看到二进制调用“strcpy”,这可能只是缓冲区溢出。如下图所示,通过检查main函数,我发现strcpy被调用。
原因有三:
首先,它会检查我是否提供了命令行参数;
其次,它会将用户输入复制到局部变量中,并打印输入的内容;
最后,它会告诉我“再试一次”然后返回。幸运的是,strcpy不检查其输入大小,否则,将导致栈缓冲区溢出。
构建利用
和x86二进制程序中的情况类似,我会先用一个大的参数运行调试器中的二进制文件来验证是否会发生溢出。
为此,我在我的QEMU VM上启动了一个gdbserver,并使用JEB的调试器接口(参见调试手册了解更多信息)。在MIPS ISA中,来自例程调用的返回地址存储在名为$ra的特定寄存器中,该寄存器也通过栈填充,就像通常在x86上看到的那样,然后跳转到保存的返回地址。
在我的二进制文件中,我通过提供一个大的参数(一系列0x4F字节)来确认返回地址是可以被用户控制的,并在strcpy调用后显示寄存器状态:
我检查了以下我重构的栈框架,以计算适当的填充。你可以使用你选择的函数中的Ctrl+Alt+k快捷方式访问该视图。我将变量buf的类型更改为变量开始和下一个之间的所有可用大小的char数组,其中有200字节。
实际上,变量var04和var08是保存的返回地址和主函数保存的帧指针。结果发现,这个偏移量是204字节,因为我用200字节填充缓冲区,并用另外四个字符覆盖保存的帧指针。让我尝试以下漏洞:
#!/usr/bin/python
padding = "O"* 204
dat_shell_addr = "x50x09x40" # Partial overwrite with little-endian arch
payload = padding + dat_shell_addr
with open("input", "wb") as f:
f.write(payload
漏洞利用过程
令人惊讶的是,我的虚拟攻击使位于地址0x400970中的程序segfaults出现在dat_shell例程中。来看看JEB中的这个地址:
我可以在上图中看到地址的存储器访问吗,该地址是通过将偏移量0x801C添加到全局指针寄存器$gp而计算出的结果。但这里的问题是$gp最初设置在$t9寄存器的程序开头(见上图的0x4000958)。
那么$t9中的值来自哪里?通常在MIPS(调用约定)上调用例程:$t9寄存器首先设置为目标例程的地址,然后再传达到例如jalr $t9指令(请参阅MIPS ISA 的第50页)。全局指针$gp然后用$t9进行初始化,用于计算各种偏移量,特别是将调用的其他函数,因此必须保证绝对正确。
换句话说,如果$t9的值在执行此例程时不是dat_shell的地址,则在例程执行期间存在无效内存访问的可能性就会变得很大。为了构建一个成功的漏洞利用,我需要先从栈中加载任意值到$t9,然后再进行传递,因为它是一个真正的函数调用。
为了做到这一点,我需要一个“gadget”,在搜索这个gadget时,我首先检查使用“libs”调试器命令加载的动态库。
幸运的是,我有三个库以固定的内存地址加载:libc.so.0,libgcc_s.so.0和ld-uClibc.so.0。
JEB的ROP Gadget查找插件
使用Gadget是构建返回导向编程技术(Return-Oriented Programming,ROP)漏洞的常见方法,所以我决定开发一个Gadget查找插件。而且,我不是从本机指令中搜索Gadget,而是决定使用JEB中间表示(IR),例如我可以公开的在JEB处理的所有架构上找到Gadget。
最终的结果是,当在JEB中加载libc.so.0,libgcc_s.so.0和ld-uClibc.so.0库时,该插件会创建一个包含所有Gadget的视图:
输出没有重复的Gadget,按字母顺序排列,以方便查找有趣的Gadget。
那么,它是如何工作的?使用JEB的API,插件将本机代码转换为我的反编译管道第一阶段使用的IR。在这个阶段,本地指令的所有缺点都被暴露出来了,并且还没有进行优化。
要查找Gadget,以分支结尾的一系列指令,我只需在程序计数器寄存器中搜索指定并向后迭代,直到该寄存器上的另一个赋值为止。最后一步是过滤掉相对转移,不过这一步在漏洞利用中是不可能真正控制的。这样我就得到了一个很好的ROPGadget列表。
同样,因为它仅使用IR是,所以该方法适用于所有架构。例如,以下就是运行在ARMv7二进制文件的相同代码:
公开的代码可以在这里找到。
现在在libc库使用我刚刚找到的插件,我发现以下Gadget在偏移0x6b20处:
它将栈顶部的值复制到$t9寄存器中,并分支到$t9寄存器。
因此,该方法就是使用易受攻击的strcpy来首先执行这个Gadget,这样dat_shell地址将被调用为正常的例程调用。在我的测试设备上停用地址空间布局随机化(ASLR)后,我可以使用之前找到的libc基地址进行漏洞利用。最后的漏洞看起来像这样:
现在开始漏洞利用。
至此为止,我利用我的JEB ROPGadget查找器已在第一个栈缓冲区成功实现了溢出。下面,让我们来看看第二和第三个缓冲区溢出。
第二缓冲区溢出过程与第一个一样,首先进行漏洞利用环境设置
首先对文件进行分析:
接下来,我会检查主要函数。
它看起来与第一个缓冲区溢出几乎完全相同,只有在strcpy()调用中会出现不同的缓冲区大小。在确认我没有获取相关函数后,我可以重定向执行。
构建利用
由于栈是可执行的,借助易受攻击的strcpy(),我可以写一个相当大的缓冲区(508字节)。
首先,我从shellstorm中获取了一个MIPS shellcode,然后我将它翻译成了little-endian,这是为MIPSEL编译的目标二进制文件。接下来,我需要找到跳转的确切的栈地址。为了简化进程,我决定在shellcode前添加 NOP sled前缀。
为了构建NOP sled,我不能简单地使用MIPS NOP指令,因为它已被编码为四个空字节,因此不能用strcpy()复制。使用Keystone汇编器,我搜索了一个等效的指令,最后使用xor$ t0,$t0,$t0,其编码不包含空字节。
我只需要将所有的部分合并在一起,就会有完整的漏洞利用代码:
可以看到shellcode已成功执行,现在我有一个shell!
第三个缓冲区溢出
类似于第二个缓冲区溢出,但是涉及一个用开放的网络socket来接收用户输入:
它以常用的socket样板代码开始,并绑定在指定为命令行参数的端口上。接受连接后,它将读取500字节,并发回用500字节输入格式化的字符串“nom nom nom, you sent me %s”。
该漏洞来自很小的sprintf()缓冲区,它只有52个字节长,如你在JEB stackframe中看到的那样:
我的策略与之前的漏洞类似,不过shellcode将是一个反向shell。
幸运的是,Jacob Holcomb已经对该方法有了详细的介绍,唯一的缺点是它连接到的IP是硬编码的:
为了方便使用这个shellcode,我添加了一条不能指向127.0.0.1或任何包含空字节的IP地址的指令。为了确保它的工作原理和调试偏移量,我会在JR $RA指令之前设置断点(Ctrl+B)并逐步用我的shellcode来运行JEB调试器中的漏洞。
然后我可以使用stepo调试器命令(或使用F6快捷方式),跳转到内存代码。
让我在端口31337上启动一个带有netcat的监听socket,并确认有一个shell:
原文发布时间为:2017年8月25日
本文作者:luochicun
本文来自云栖社区合作伙伴嘶吼,了解相关信息可以关注嘶吼网站。