November 12, 2012

An Example for Hidden NULL Pointer Bug

When performing security testing of the native application I often attach Windbg to the target process. The obvious benefit of this methodology is that when an exception occurs it's possible to immediately analyze and save the state information of the program. There is one more important benefit, and that is you can get additional information of the running application if you set certain options beforehand. For example, sxn ud command sets up the debugger to send notification when a module is unloaded. I find very useful to analyze Windbg log during security testing, rather than just relying to find exception or memory corruption.

Some time ago, during testing Internet Explorer 9, I noticed that Windbg screen had been flooded with the following messages.
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.
The application continued running without displaying more of these messages and there was no sign the integrity of the application corrupted. My first thought was it's the result of one of Windbg commands I set up earlier just before executing the test. The second thought was that it is unlikely because it seems to be a CRT error message, that most likely comes from the runtime DLL. I knew there is a way to send message to the debugger from an executable and that is to call OutputDebugString function.

I set up a breakpoint for OutputDebugString, and rerun the test. Here is the call stack when the breakpoint hit.
0:005> k
Child-SP          RetAddr           Call Site
00000000`04448388 000007fe`fdfd2478 KERNELBASE!OutputDebugStringA
00000000`04448390 000007fe`fdfd249b msvcrt!invoke_watson+0x98
00000000`044488d0 000007fe`fdfb0439 msvcrt!invalid_parameter+0x13
00000000`04448910 00000000`6027290f msvcrt!wcsnicmp+0x40
00000000`04448950 00000000`6000b522 MSHTML!CMultimediaLog::ExtractVideoData+0x8f
00000000`04448a30 00000000`600077ba MSHTML!CDoc::ExecHelper+0x4308
00000000`044498b0 000007fe`f1a22805 MSHTML!CDoc::Exec+0x2a
00000000`04449900 000007fe`f1a222f1 IEFRAME!CDocObjectHost::_PopulateOnlineHistoryData+0x141
00000000`04449990 000007fe`f1a21ecb IEFRAME!CDocObjectHost::_UpdateHistoryAndIntSiteDB+0x229
00000000`0444cb10 000007fe`f1a22001 IEFRAME!CDocObjectHost::_OnReadyState+0x21b
00000000`0444cda0 000007fe`f1a220bc IEFRAME!CDocObjectHost::_OnChangedReadyState+0xd1
00000000`0444ce70 00000000`5ff33c27 IEFRAME!CDocObjectHost::OnChanged+0x1c
00000000`0444cea0 00000000`5ff31380 MSHTML!CBase::FirePropertyNotify+0x3a3
00000000`0444cf30 00000000`5ff7f874 MSHTML!CMarkup::SetReadyState+0x41a
00000000`0444cfb0 00000000`5ff323b1 MSHTML!CMarkup::OnLoadStatusDone+0x3eb
00000000`0444d070 00000000`5ff7ef3f MSHTML!CMarkup::OnLoadStatus+0xb2
00000000`0444d0a0 00000000`5ff2f4e0 MSHTML!CProgSink::DoUpdate+0x5f5
00000000`0444d530 00000000`5fffb468 MSHTML!GlobalWndOnMethodCall+0x18b
00000000`0444d5c0 00000000`76de9bd1 MSHTML!GlobalWndProc+0x36c
00000000`0444d640 00000000`76de98da USER32!UserCallWinProcCheckWow+0x1ad
00000000`0444d700 000007fe`f198af5e USER32!DispatchMessageWorker+0x3b5
00000000`0444d780 000007fe`f1937754 IEFRAME!CTabWindow::_TabWindowThreadProc+0x9c1
00000000`0444fbe0 00000000`771553b3 IEFRAME!LCIETab_ThreadProc+0x39f
00000000`0444fd80 000007fe`f1918dcb iertutil!CIsoScope::RegisterThread+0x10f
00000000`0444fdb0 00000000`76ee652d IEFRAME!Detour_DefWindowProcA+0x97
00000000`0444fdf0 00000000`774dc521 kernel32!BaseThreadInitThunk+0xd
00000000`0444fe20 00000000`00000000 ntdll!RtlUserThreadStart+0x1d
Looking at the parameter of OutputDebugString I saw the followings.
0:005> db @rcx
000007fe`fe014110  49 6e 76 61 6c 69 64 20-70 61 72 61 6d 65 74 65  Invalid paramete
000007fe`fe014120  72 20 70 61 73 73 65 64-20 74 6f 20 43 20 72 75  r passed to C ru
000007fe`fe014130  6e 74 69 6d 65 20 66 75-6e 63 74 69 6f 6e 2e 0a  ntime function..
000007fe`fe014140  00 00 00 00 3a 20 00 00-0a 00 0a 00 00 00 00 00  ....: ..........
000007fe`fe014150  2e 00 2e 00 2e 00 00 00-00 00 00 00 00 00 00 00  ................
000007fe`fe014160  4d 00 69 00 63 00 72 00-6f 00 73 00 6f 00 66 00  M.i.c.r.o.s.o.f.
000007fe`fe014170  74 00 20 00 56 00 69 00-73 00 75 00 61 00 6c 00  t. .V.i.s.u.a.l.
000007fe`fe014180  20 00 43 00 2b 00 2b 00-20 00 52 00 75 00 6e 00   .C.+.+. .R.u.n.
This confirms this function sends the message to the debugger. We also see that OutputDebugString is called from wcsnicmp that is called from CMultimediaLog::ExtractVideoData.

I set up a breakpoint just before we call wcsnicmp.
0:005> ub MSHTML!CMultimediaLog::ExtractVideoData+0x8f
MSHTML!CMultimediaLog::ExtractVideoData+0x69:
00000000`602728e9 7409            je      MSHTML!CMultimediaLog::ExtractVideoData+0x74 (00000000`602728f4)
00000000`602728eb 488b01          mov     rax,qword ptr [rcx]
00000000`602728ee ff9020050000    call    qword ptr [rax+520h]
00000000`602728f4 4c8b6c2428      mov     r13,qword ptr [rsp+28h]
00000000`602728f9 488d15480f6900  lea     rdx,[MSHTML!`string' (00000000`60903848)]
00000000`60272900 41b807000000    mov     r8d,7
00000000`60272906 498bcd          mov     rcx,r13
00000000`60272909 ff1591ca5e00    call    qword ptr [MSHTML!_imp__wcsnicmp (00000000`6085f3a0)]
The place of the breakpoint is the highlighted line above.

Here we are when the breakpoint is hit.
Breakpoint 2 hit
MSHTML!CMultimediaLog::ExtractVideoData+0x89:
00000000`60272909 ff1591ca5e00    call    qword ptr [MSHTML!_imp__wcsnicmp (00000000`6085f3a0)] ds:00000000`6085f3a0={msvcrt!wcsnicmp (000007fe`fdf93330)}
Note that wcsnicmp takes three parameters: string1, string2, and count. These describes null-terminated strings and number of characters to compare.

I checked the parameters of wcsnicmp and saw the followings.
0:005> r rcx;r rdx;r r8
rcx=0000000000000000
rdx=0000000060903848
r8=0000000000000007
string2 and count both look healthy however string1 is NULL but the API documentation expects a string.

To make sure everything is ok with string2 let's check.
0:005> du @rdx
00000000`60903848  "http://"
It's fine, so the only problem with parameter string1 and the problem is that a NULL pointer is passed rather than a pointer to the string.

Microsoft long time ago added security enhancements in the CRT library. One of them involves to call invalid parameter handler which, for example, checks for NULL pointer parameters, and we terminate gracefully rather than with invalid memory access exception.

As you can see the return value is the highest positive value that can be represented in signed 32-bit. If the application is not prepared to handle this edge value it could cause problem later on the execution. Fortunately, that was not the case with Internet Explorer.
0:005> g 6027290f
MSHTML!CMultimediaLog::ExtractVideoData+0x8f:
00000000`6027290f baffff0000      mov     edx,0FFFFh
0:005> r eax
eax=7fffffff
The NULL check suppresses the crash but the application likely having problems managing the lifetime of objects.

Developers are advised to avoid the use of wcsnicmp() function as it's deprecated. Please check references what to use instead.

References

Security Features in the CRT
  This blog is written and maintained by Attila Suszter. Read in Feed Reader. Advert is experimentally shown.