Introduction

Problem description:

i386 Linux guest, e.g. Ubuntu precise with kernel 3.2.0-29-generic, can be crashed by invocation of software interrupt 0x8 from userspace. Crash occurs only when guest is running inside VirtualBox < 4.2.0-RC3, e.g. 4.1.12-dfsg-2ubuntu0.1 on 64bit kernel, e.g. ubuntu precise amd64 3.2.0-29-generic. The crash occurs due to a CPU-emulation bug in VirtualBox when calling a task-gate via IDT with insufficient privileges (CPL) and processor is missing VT-x / AMD-V extensions. On real processors or within guest running on virtualization-enhanced processors, that would result in a general protection fault (GPF), in VirtualBox CPL is not checked and incomplete task-switch to double-fault-handler is performed.

Invocation on Linux results in DOS, privilege escalation might be possible on systems where suitable ring-0 task-gates exists, although Oracle says, that due to other measures in VirtualBox code, it is not possible to execute arbitrary ring-0 code.

Affected systems:

Method

Results

Invocation of software interrupt causes kernel crash, but using serial console, a stack trace of the event can be recorded. The guest kernel gets stuck in an endless loop printing out stack traces. The first ones are:

* Starting X display manager xdm [ OK ] * Stopping System V runlevel compatibility [ OK ] [ 126.784103] BUG: unable to handle kernel NULL pointer dereference at 00000014 [ 126.784103] IP: [<c104b664>] vprintk+0x14/0x440 [ 126.784103] *pde = 00000000 [ 126.784103] Oops: 0000 [#1] SMP [ 126.784103] Modules linked in: xt_multiport xt_hashlimit xt_conntrack ..... [ 126.784103] [ 126.784103] Pid: 1175, comm: Test Not tainted 3.2.0-29-generic #46-Ubuntu innotek GmbH VirtualBox [ 126.784103] EIP: 0060:[<c104b664>] EFLAGS: 00004092 CPU: 0 [ 126.784103] EIP is at vprintk+0x14/0x440 [ 126.784103] EAX: c1700e40 EBX: cf096000 ECX: c1700e40 EDX: c18f4abc [ 126.784103] ESI: 00000000 EDI: c18f4abc EBP: c18f4aa0 ESP: c18f4a2c [ 126.784103] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 [ 126.784103] Process Test (pid: 1175, ti=c18f4000 task=cd449960 task.ti=cd444000) [ 126.784103] Stack: [ 126.784103] 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 126.784103] 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 126.784103] 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 126.784103] Call Trace: [ 126.784103] [<c155ebcf>] printk+0x2d/0x2f [ 126.784103] BUG: unable to handle kernel NULL pointer dereference at 00000c70 [ 126.784103] IP: [<c10054c0>] print_context_stack+0x80/0x100 [ 126.784103] *pde = 00000000 [ 126.784103] Oops: 0000 [#2] SMP [ 126.784103] Modules linked in: xt_multiport xt_hashlimit xt_connt.... [ 126.784103] [ 126.784103] Pid: 1175, comm: Test Not tainted 3.2.0-29-generic #46-Ubuntu innotek GmbH VirtualBox [ 126.784103] EIP: 0060:[<c10054c0>] EFLAGS: 00000083 CPU: 0 [ 126.784103] EIP is at print_context_stack+0x80/0x100 [ 126.784103] EAX: 00000000 EBX: c18f4aa4 ECX: 00000032 EDX: c18f4000 [ 126.784103] ESI: 00000000 EDI: c18f4ab0 EBP: c18f4820 ESP: c18f47fc [ 126.784103] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 [ 126.784103] Process Test (pid: 1175, ti=c18f4000 task=cd449960 task.ti=cd444000) [ 126.784103] Stack: [ 126.784103] c155ebcf c18f5ffc c18f4ab0 ffffe000 c18f4000 c18f4ab0 c18f4a2c c157d700 [ 126.784103] c18f4000 c18f484c c100469f c157d700 c1703ae3 00000000 c18f4838 00000000 [ 126.784103] c18f484c 00000000 c18f49f0 c1703ae3 c18f4870 c100560c c18f4aa0 c157d700 [ 126.784103] Call Trace: [ 126.784103] [<c155ebcf>] ? printk+0x2d/0x2f [ 126.784103] BUG: unable to handle kernel NULL pointer dereference at 00000c70 [ 126.784103] IP: [<c10054c0>] print_context_stack+0x80/0x100 [ 126.784103] *pde = 00000000 [ 126.784103] Oops: 0000 [#3] SMP [ 126.784103] Modules linked in: xt_multiport xt_hashlimit xt_con..... [ 126.784103] [ 126.784103] Pid: 1175, comm: Test Not tainted 3.2.0-29-generic #46-Ubuntu innotek GmbH VirtualBox ...

Checking the source reveals, that the message BUG: unable to handle kernel NULL pointer dereference at 00000014 is not completely correct, the disassembly shows, that the stack canary should be read, but segmentation unit does not provide correct mapping for gs:

0xc104b650: push %ebp 0xc104b651: mov %esp,%ebp 0xc104b653: push %edi 0xc104b654: push %esi 0xc104b655: push %ebx 0xc104b656: sub $0x68,%esp 0xc104b659: lea %ds:0x0(%esi,%eiz,1),%esi 0xc104b65e: mov 0x8(%ebp),%ecx 0xc104b661: mov 0xc(%ebp),%edi 0xc104b664: mov %gs:0x14,%eax

Since the interrupt is the trigger, the current interrupt descriptor table might be the key to understand the crash. From outside guest, VirtualBox debugger shows, that interrupt 8 is handled by task gate:

VBoxDbg> di 0000 Int32 Sel:Off=0060:c1574374 DPL=0 P 0001 Int32 Sel:Off=0060:c157440c DPL=0 P 0002 Int32 Sel:Off=0060:c1574464 DPL=0 P 0003 Int32 Sel:Off=0060:c1574584 DPL=3 P 0004 Int32 Sel:Off=0060:c1574324 DPL=3 P 0005 Int32 Sel:Off=0060:c1574330 DPL=0 P 0006 Int32 Sel:Off=0060:c157433c DPL=0 P 0007 Int32 Sel:Off=0060:c1574310 DPL=0 P 0008 TaskG TSS=00f8 DPL=0 P 0009 Int32 Sel:Off=0060:c1574348 DPL=0 P ...

VBoxDbg> dt 0xf8 00f8 TSS32 at %00000000c17c5800 LB 206c (min=0068) eax=00000000 bx=00000000 ecx=00000000 edx=00000000 esi=00000000 edi=00000000 eip=c1022480 esp=c18f4ae0 ebp=00000000 cs=0060 ss=0068 ds=007b es=007b fs=00d8 gs=0000 eflags=00000082 ss:esp0=0068:c18f4ae0 ss:esp1=0000:00000000 ss:esp2=0000:00000000 prev=0000 ldtr=0000 cr3=018eb000 debug=0 iomap=8000 0060:c1022480 55 push ebp No interrupt redirection bitmap (-0xffff8068) No I/O bitmap (-0xffff8068)

Checking task gate segment 0xf8 shows, that gs is set to 0 (no segment), so any access of gs will cause an exception. The eip points to c1022480 t doublefault_fn according to System.map. This seems to be consistent with kernel code:

./arch/x86/kernel/traps.c: set_intr_gate_ist(8, &double_fault, DOUBLEFAULT_STACK);

For cross checking, the idt was also read from within guest using symbol idt_table from System.map. The entry for interrupt 0x8 is consistent with the information reported by vbox debugger:

(gdb) x/256x 0xc17c5000 0xc17c5000: 0x00604374 0xc1578e00 0x0060440c 0xc1578e00 0xc17c5010: 0x00604464 0xc1578e00 0x00604584 0xc157ee00 0xc17c5020: 0x00604324 0xc157ee00 0x00604330 0xc1578e00 0xc17c5030: 0x0060433c 0xc1578e00 0x00604310 0xc1578e00 0xc17c5040: 0x00f80000 0x00008500 0x00604348 0xc1578e00

It seems, that the int 0x8 idt entry is really broken, but it is not clear why. Possible reasons (among others) could be:

Discussion on VirtualBox mailing list showed, that the last option is the root cause: CPU-emulation does not check if calling the task-gate is really allowed from current privilege level (see upstream fix 43068).

Discussion

Analysis showed, that CPU-emulation for CPUs without VT-x / AMD-V features contains a bug in src/recompiler/target-i386/op_helper.c which allowed to call a task-gate without having correct permissions to do so.

On Linux guests with the task-gate at interrupt 0x8, this seems to cause only DOS-condition, due to calling the double-fault handler from outside kernel. Under different conditions or on other operating systems using task-gates, privilege escalation might be possible, although very unlikely, according to comment from Oracle.

Timeline

Material, References

Reproducing, patching:

Finding cause, discussion, bug reports:

Vulnerability tracking databases, advisories:

Last modified 20171228
Contact e-mail: me (%) halfdog.net