Title: UnMakeSIS local buffer overflow Release Date: xx. February 2006 Author: Dennis Elser (dennis backtrace de) Vendor: AtzSoft (http://www.atz-soft.de) Vendor Status: 29. January - vendor contacted 30. January - vendor reply 05. February - vender fixed the issue Product: UnMakeSIS Affected Version: 0.9 (and probably all versions up to 1.2) Platform: Microsoft Windows Architecture: IA32 Vulnerability: Missing boundary checks Risk: High (Local code execution) Discovery: 07. May 2005 Impact: A malicious user could run arbitrary code Remediation: Update to version 1.3 -------------------------------------------------------------------------- Background: ----------- Bug Description: ---------------- UnMakeSIS will cause a stack overflow upon loading corrupted .sis files. If successfully exploited, a malicious user can run arbitrary code. Technical Description / Proof of Concept: ----------------------------------------- The "app name" field, which is a unicode string read from an external file is passed to the following function, then passed to WideCharToMultiByte() for a conversion into a multibyte string. The function consists of the following bug: 1.) In the code below, "cchWideChar" is initialized with a value read from a .sis file and therefore under control of the attacker. 2.) The size for the static destination buffer is dynamically calculated in dependency of cchWideChar, without being bounds-checked. Here's the faulty code: .text:00408003 ; int __stdcall evil_function(LPSTR lpMultiByteStr,int) .text:00408003 evil_function proc near ; CODE XREF: sub_40557C+127p .text:00408003 ; sub_406B74+13Dp ... .text:00408003 .text:00408003 cchWideChar = dword ptr -4 .text:00408003 lpMultiByteStr = dword ptr 8 .text:00408003 arg_4 = dword ptr 0Ch .text:00408003 .text:00408003 push ebp .text:00408004 mov ebp, esp .text:00408006 push ecx .text:00408007 mov eax, [ecx+1Ch] .text:0040800A push ebx .text:0040800B mov ebx, [ecx+4] .text:0040800E mov ecx, [eax+40h] .text:00408011 push esi .text:00408012 movsx esi, word ptr [eax+12h] .text:00408016 add ecx, ebx .text:00408018 mov eax, esi .text:0040801A sub eax, [ebp+arg_4] .text:0040801D push edi .text:0040801E mov eax, [ecx+eax*4-4] ; get length of string (read from sis file) .text:0040801E ; (under control of the attacker) .text:00408022 mov [ebp+cchWideChar], eax ; store length in local stack variable (cchWideChar). * Mistake number one happened here: cchWideChar is initialized with a value under control of the attacker. .text:00408025 cdq .text:00408026 sub eax, edx .text:00408028 mov edi, eax ; edi = eax = ccWideChar .text:0040802A xor eax, eax .text:0040802C push eax ; lpUsedDefaultChar .text:0040802D push eax ; lpDefaultChar .text:0040802E sar edi, 1 ; edi = cchWideChar / 2 .text:00408030 push edi ; cchMultiByte = cchWideChar / 2 * Mistake number two happened here: cchMultiByte (which is the size of the destination buffer) is assigned the length of cchWideChar divided by two. (The destination buffer is limited to a size of 0xC8 bytes - shown later in the disassembly of "sub_4072FB"). .text:00408031 push [ebp+lpMultiByteStr] ; lpMultiByteStr .text:00408034 lea edx, [esi+esi] .text:00408037 sub edx, [ebp+arg_4] .text:0040803A push [ebp+cchWideChar] ; cchWideChar .text:0040803D mov ecx, [ecx+edx*4-4] .text:00408041 add ecx, ebx .text:00408043 push ecx ; lpWideCharStr .text:00408044 push eax ; dwFlags .text:00408045 push 0FDE9h ; CodePage .text:0040804A call ds:WideCharToMultiByte * WideCharToMultiByte overwrites the stack frame of "sub_4072FB" if cchWideChar is greater than 400 (0x190). .text:00408050 mov eax, [ebp+lpMultiByteStr] .text:00408053 mov byte ptr [edi+eax], 0 .text:00408057 pop edi .text:00408058 pop esi .text:00408059 pop ebx .text:0040805A leave .text:0040805B retn 8 .text:0040805B evil_function endp The following function passes a pointer of the destination buffer (which has a static size) for WideCharToMultiByte() to evil_function: .text:004072FB sub_4072FB proc near ; CODE XREF: sub_404C10+7Fp .text:004072FB .text:004072FB MultiByteStr = byte ptr -0C8h .text:004072FB arg_0 = dword ptr 8 .text:004072FB arg_4 = dword ptr 0Ch .text:004072FB .text:004072FB push ebp .text:004072FC mov ebp, esp .text:004072FE sub esp, 0C8h .text:00407304 push [ebp+arg_4] ; int .text:00407307 lea eax, [ebp+MultiByteStr] .text:0040730D push eax ; lpMultiByteStr .text:0040730E mov ecx, offset dword_40C3B0 .text:00407313 call evil_function [...snip...] .text:0040732F leave .text:00407330 retn 8 .text:00407330 sub_4072FB endp MultiByteStr is the buffer (limited to 0xC8 bytes) passed to evil_function. In a proof of concept .sis file, the size of the destination buffer was calculated to be 0xFF, which made WideCharToMultiByte() overflow "MultiByteStr". -------------------------------------------------------------------------- Dennis Elser, 04.02.2006