xorl %eax, %eax

CVE-2009-2970: UiTV UiPlayer ActiveX Stack Overflow

leave a comment »

This bug was discovered and reported by Yu Yang of NSFOCUS Security Team. As the author state in the advisory, this issue affects UiCheck.dll releases prior to 1.0.0.7. So, I used 1.0.0.6 version of that DLL and let’s see what I’ve found…

.text:10002390
.text:10002390 ; int __stdcall sub_10002390(int, LPCWSTR lpWideCharStr, int)
.text:10002390 sub_10002390    proc near               ; DATA XREF: .rdata:1001391C^Yo
.text:10002390                                         ; .rdata:10013F24^Yo
.text:10002390
.text:10002390 var_4A8         = byte ptr -4A8h
.text:10002390 CodePage        = dword ptr -4A4h
.text:10002390 puLen           = dword ptr -4A0h
.text:10002390 lpBuffer        = dword ptr -49Ch
.text:10002390 var_498         = dword ptr -498h
.text:10002390 dwHandle        = dword ptr -494h
.text:10002390 SubBlock        = byte ptr -490h
.text:10002390 SubKey          = byte ptr -390h
.text:10002390 ValueName       = byte ptr -290h
.text:10002390 var_194         = byte ptr -194h
.text:10002390 var_190         = byte ptr -190h
.text:10002390 var_11B         = byte ptr -11Bh
.text:10002390 Data            = byte ptr -110h
.text:10002390 Str             = byte ptr -0Ch
.text:10002390 var_4           = dword ptr -4
.text:10002390 lpWideCharStr   = dword ptr  0Ch
.text:10002390 arg_8           = dword ptr  10h
.text:10002390

This is the buggy handling routine and here is how it starts it code…

.text:10002390                 push    ebp
.text:10002391                 lea     ebp, [esp-428h]
.text:10002398                 sub     esp, 4A8h
.text:1000239E                 mov     eax, dword_10017360
.text:100023A3                 push    ebx
.text:100023A4                 push    esi
.text:100023A5                 push    edi
.text:100023A6                 mov     [ebp+428h+var_4], eax
.text:100023AC                 call    Target
.text:100023B2                 mov     ebx, [ebp+428h+lpWideCharStr]
.text:100023B8                 mov     [ebp+428h+CodePage], eax
.text:100023BB                 xor     eax, eax
.text:100023BD                 cmp     ebx, eax
.text:100023BF                 mov     [ebp+428h+dwHandle], eax
.text:100023C2                 jz      short loc_10002404

First of all, EBP is loaded with ‘[esp-428h]’ and stack pointer is set accordingly. Then EAX register is initialized with ‘dword_10017360’ which is later moved to ‘[ebp+428h+var_4]’ as the fourth argument of ‘Target’ callback function. When this returns, EBX is initialized with ‘lpWideCharStr’ and the returned value of the callback function is used to set the ‘CodePage’ value. EAX register is zeroed out using a simple XOR logical operation and the result compared. Then, it initializes ‘dwHandle’ to the value of EAX and if it’s zero, it will perform a short jump to ‘loc_10002404’ which will initialize EDX and EBX like this:

.text:10002404
.text:10002404 loc_10002404:                           ; CODE XREF: sub_10002390+32^Xj
.text:10002404                                         ; sub_10002390+70^Xj
.text:10002404                 lea     edx, [ebp+428h+ValueName]
.text:1000240A                 lea     ebx, [ebx+0]

Assuming that this is not the case, the following will be executed:

.text:100023C4                 push    ebx             ; lpString
.text:100023C5                 call    ds:lstrlenW
.text:100023CB                 lea     edi, [eax+eax+2]
.text:100023CF                 mov     eax, edi
.text:100023D1                 add     eax, 3
.text:100023D4                 and     eax, 0FFFFFFFCh
.text:100023D7                 call    __alloca_probe

This code performs a call to Unicode lstrlenW() routine passing EBX (that contains lpString) to it as an argument. It loads ‘[eax+eax+2]’ to EDI register and then moves that value back to EAX and adds 3 to it. Finally, it performs a logical AND mask with 0x0FFFFFFF to avoid negative numbers and calls __alloca_probe() to ensure that there is enough space in the stack for allocation.
Let’s continue…

.text:100023DC                 mov     esi, esp
.text:100023DE                 test    esi, esi
.text:100023E0                 jz      short loc_10002402

So, ESI is initialized with ESP’s value and if the test check returns zero, it will jump to ‘loc_10002402’ which is a simple code that zeroes EAX out like this:

.text:10002402
.text:10002402 loc_10002402:                           ; CODE XREF: sub_10002390+50^Xj
.text:10002402                 xor     eax, eax

However, if this is not the case. Which means that we have enough space on the stack, a call to WideCharToMultiByte() will be performed like this:

.text:100023E2                 mov     eax, [ebp+428h+CodePage]
.text:100023E5                 push    0               ; lpUsedDefaultChar
.text:100023E7                 push    0               ; lpDefaultChar
.text:100023E9                 push    edi             ; cchMultiByte
.text:100023EA                 push    esi             ; lpMultiByteStr
.text:100023EB                 push    0FFFFFFFFh      ; cchWideChar
.text:100023ED                 push    ebx             ; lpWideCharStr
.text:100023EE                 push    0               ; dwFlags
.text:100023F0                 push    eax             ; CodePage
.text:100023F1                 mov     byte ptr [esi], 0
.text:100023F4                 call    ds:WideCharToMultiByte
.text:100023FA                 neg     eax
.text:100023FC                 sbb     eax, eax
.text:100023FE                 and     eax, esi
.text:10002400                 jmp     short loc_10002404
.text:10002402 ; ---------------------------------------------------------------------------

The returned value (stored in EAX) is checked for overflow using NEG instruction. The subsequent call to SBB will subtract (with cartage) the EAX register and then perform a logical AND to it using ESI register. The short jump to ‘loc_10002404’ was described earlier, it will just execute this:

.text:10002404 loc_10002404:                           ; CODE XREF: sub_10002390+32^Xj
.text:10002404                                         ; sub_10002390+70^Xj
.text:10002404                 lea     edx, [ebp+428h+ValueName]
.text:1000240A                 lea     ebx, [ebx+0]
.text:10002410

And here is the code you’ve been waiting for…

.text:10002410 loc_10002410:                           ; CODE XREF: sub_10002390+88^Yj
.text:10002410                 mov     cl, [eax]
.text:10002412                 inc     eax
.text:10002413                 mov     [edx], cl
.text:10002415                 inc     edx
.text:10002416                 test    cl, cl
.text:10002418                 jnz     short loc_10002410

This is a simple loop that will continue copying ‘[eax]’ to ‘cl’ and ‘cl’ to the location of EDX as long as ‘cl’ is not NULL. This looks like a simple loop similar to the following (in C pseudo-code):

char *edx = ValueName;
char *ebx = lpWideCharStr;

*(char *)ebx = 0;          // As it did in loc_10002404

while ( *ebx != NULL)
{
        *(char *)edx = *(char *)ebx;
        *ebx++;
        *edx++;
}

So, it will continue copying regardless of the destination buffer’s size. However, ‘ValueName’ has static size as you can read from the stack frame (or the offsets generated by IDA in the above paste). The code that follows is of no interest. It performs some GetModuleHandleA(), GetModuleFileNameA(), GetFileVersionInfoSizeA() etc. calls. In 1.0.0.7 DLL version, this code was changed like this:

.text:10002415 loc_10002415:                           ; CODE XREF: sub_10002390+81^Xj
.text:10002415                 push    32h             ; Count
.text:10002417                 push    eax             ; Source
.text:10002418                 lea     ecx, [ebp+428h+ValueName]
.text:1000241E                 push    ecx             ; Dest
.text:1000241F                 call    _strncpy
.text:10002424                 add     esp, 0Ch
.text:10002427                 push    offset aUicheck_dll ; "UiCheck.dll"
.text:1000242C                 call    ds:GetModuleHandleA

As you can read, instead of using that simple copy loop that they did, they use _strncpy() in a manner similar to:

_strncpy(ValueName, lpWideCharStr, 0x32);

Quite simple bug and if you spend some time on that DLL you’ll see that it has a few more vulnerabilities. Anyway, this post was a result of my first attempt to use N. Economou’s TurboDiff which is really awesome plugin for IDA. :)

Written by xorl

November 1, 2009 at 18:36

Posted in bugs, Windows

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s