开启辅助访问 切换到窄版

打印 上一主题 下一主题

LinuxKernelPwn_2_KernelUAF

[复制链接]
作者:JasonSWK 
版块:
嵌入式操作系统 linux 发布时间:2021-3-4 22:30:45
391540
楼主
跳转到指定楼层
| 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本文为看雪论优秀文章

前期准备提取.ko文件解压出来并没有我们想要的驱动文件,需要我们手动提取。启动脚本如下:#!/bin/shmount -t proc none /procmount -t sysfs none /sysmount -t devtmpfs devtmpfs /devchown root:root flagchmod 400 flagexec 0/dev/consoleexec 2>/dev/consoleinsmod /lib/modules/4.4.72/babydriver.kochmod 777 /dev/babydevecho -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"setsid cttyhack setuidgid 1000 shumount /procumount /syspoweroff -d 0 -f可以看到.ko文件在/lib/modules/4.4.72/babydriver.ko。一开始cpio解压出来都是乱码,然后file了一下,发现他是个gz压缩的文件orz
提取vmlinux

注意,本题没有直接给vmlinux,所以我们需要手动提取:./extract-vmlinux ./bzImage > vmlinux #!/bin/sh# SPDX-License-Identifier: GPL-2.0-only# # extract-vmlinux - Extract uncompressed vmlinux from a kernel image## Inspired from extract-ikconfig# (c) 2009,2010 Dick Streefland ## (c) 2011 Corentin Chary #check_vmlinux{# Use readelf to check if it's a valid ELF# TODO: find a better to way to check that it's really vmlinux# and not just an elfreadelf -h $1 > /dev/null 2>&1 || return 1cat $1exit 0}try_decompress{# The obscure use of the "tr" filter is to work around older versions of# "grep" that report the byte offset of the line instead of the pattern.# That didn't work, so retry after decompression.try_decompress '\037\213\010' xy gunziptry_decompress '\3757zXZ\000' abcde unxztry_decompress 'BZh' xy bunzip2try_decompress '\135\0\0\0' xxx unlzmatry_decompress '\211\114\132' xy 'lzop -d'try_decompress '\002!L\030' xxx 'lz4 -d'try_decompress '(\265/\375' xxx unzstd# Finally check for uncompressed images or objects:check_vmlinux $img首先,调用alloc_chrdev_region(&babydev_no, 0LL, 1LL, "babydev")。 让内核分配一个尚未使用的主设备号。 其函数原型如下:*/{struct char_device_struct *cd;cd = __register_chrdev_region(0, baseminor, count, name);if (IS_ERR(cd))return PTR_ERR(cd);*dev = MKDEV(cd->major, cd->baseminor);return 0;}此时申请好了设备号。 然后调用:cdev_init(&cdev_0, &fops); 来将字符设备对应的cdev结构体与file_operations对应起来。 其中cdev结构体如下:
struct cdev {struct kobject kobj; //每个cdev都是一个kobjectstruct module *owner; //指向实现驱动的模块const struct file_operations *ops;//操作这个字符文件的方法struct list_head list;// 与 cdev 对应的字符设备文件的 inode->i_devices 的链表头dev_t dev; // 起始设备编号unsigned int count; // 设备范围号大小} __randomize_layout;后面调用v7 = device_create(v5, 0LL, babydev_no, 0LL, "babydev"); 创建一个设备,并注册它到sysfs中:device_create|+ -- kzalloc struct device|+---device_register|+-----device_initialize|+-----device_addbabyopen

int __fastcall babyopen(inode *inode, file *filp){__int64 v2; // rdx__int64 v3; // rdx_fentry__(inode, filp, v2);babydev_struct.device_buf = (char *)kmem_cache_alloc_trace(kmalloc_caches[6], 0x24000C0LL, 0x40LL);babydev_struct.device_buf_len = 0x40LL;printk("device open\n", 0x24000C0LL, v3);return 0;}整体来说就是开了一个0x40的空间,然后记录他的起始地址在babydev_struct.device_buf,大小在:babydev_struct.device_buf_lenbabyrelease(调用close的时候会调用这个)

release会把开的空间放掉。babywrite和babywrite

就是利用copy from user来写和读。babyioctl

首先检查command是不是0x10001。

  • 如果是:释放之前的空间,然后利用kmalloc申请一个新的,再设置size
  • 如果不是:输出提示语句然后返回。
漏洞分析通过init脚本,我们可以发现——flag文件在root底下。但是如果我们直接登录的话默认是ctf用户,所以目标就是完成提权。#!/bin/sh/ $ ls -a rootls: can't open 'root': Permission denied/ $程序的漏洞点在于,babydev_struct 是一个全局且唯一的结构体,如果我们两次打开他,会造成第二次的指针覆盖上去,但是如果我们free掉第一次的,通过close(fd1),那么此时实际kfree的是第二次申请的空间,但是第二次的指针仍然指向他,且fd2没有被close,这就达到了UAF。

更加具体的,我们知道一个进程的权限是cred结构体维持的,如果我们恰好能通过ioctl修改这一块被UAF的空间的大小为cred结构体的大小。那么,如果我们fork一个新进程,就会重新把这一块处于free状态并且大小刚好的空间申请出来,然后我们再通过第二次申请的指针修改cred结构体中的gid,uid为0,达到提权的目的即可。

struct cred {atomic_t usage;#ifdef CONFIG_DEBUG_CREDENTIALSatomic_t subscribers; /* number of processes subscribed */void *put_addr;unsigned magic;#define CRED_MAGIC 0x43736564#define CRED_MAGIC_DEAD 0x44656144#endifkuid_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_KEYSunsigned 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_SECURITYvoid *security; /* subjective LSM security */#endifstruct 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 */} __randomize_layout;exp在babyopen断下来,看到启动后babydev_struct在内核中的位置是:0xffffffffc00024d00xffffffffc00024d8第一次open时申请的如下:第二次open结束时申请如下:此时发生了指针的覆盖
babydev_struct.device_buf = 0xffff880000b66d80babydev_struct.device_buf_len = 0x40接下来调用ioctl改申请的大小为cred结构体的大小:可以看到在ioctl运行结束时,babydev_struct.device_buf_len已经变成了0xa8。

  • 当我们close(fd1)时触发了babyrelease:由于指针被第二次申请的覆盖了,实际上close(fd1)时是free的第二次申请的chunk(此时大小已经是0xa8)
  • 进入babywrite中,此时fork成功。babydev_struct.device_buf存储的地址已经被当作cred结构体使用
0x3e8就是1000babywrite中调用copy_from_user完毕后,提权结束效果如下:

至此,完成了Kernel UAF的提权 : ) PWN!!!参考http://blog.csdn.net/weixin_42314225/article/details/81112217

- End -

看雪ID:Ta1pax0s
*本文由看雪论坛 Ta1pax0s 原创,转载请注明来自看雪社区。

推荐文章++++
公众号ID:ikanxue
官方微博:看雪安全求分享
求点赞
求在看

本帖子中包含更多资源

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

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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