The blog continues at suszter.com/ReversingOnWindows

November 19, 2012

An Attempt to Make a Read Access Violation Exploitable

This is the analysis of an attempt to make a read access violation exploitable. The bug is found in the 64-bit version of Internet Explorer 9 (9.0.8112.16421).

The crash happens when trying to open the SVG file with the below content. Note, that CDATA (stands for charachter data) doesn't have any element content leading to crash.
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <![CDATA[]]>
</svg>
These are the registers and the faulting instruction when the read access violation occurs.
0:005> r
rax=0000000000000000 rbx=00000000042bc2b0 rcx=00000000ffffffff
rdx=0000000000000000 rsi=0000000000000002 rdi=00000000042bbdc0
rip=000000005bd9aff5 rsp=00000000042bc4d0 rbp=00000000002844b0
 r8=0000000000000000  r9=0000000000000084 r10=000000005c7c1b00
r11=000000000eb1d7a0 r12=00000000042bc570 r13=0000000000000000
r14=00000000002844b0 r15=0000000000000001
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010244
MSHTML!CHtmSpaceParseCtx::AddNonspaces+0x134:
00000000`5bd9aff5 410fb70c4c      movzx   ecx,word ptr [r12+rcx*2] ds:00000002`042bc56e=????
This is the call stack. It's not used to support any of claims but it's pasted for information only.
0:005> k
Child-SP          RetAddr           Call Site
00000000`042bc4d0 00000000`5bc7c249 MSHTML!CHtmSpaceParseCtx::AddNonspaces+0x134
00000000`042bc530 00000000`5bc7c153 MSHTML!CHtmCrlfParseCtx::AddTextIE9+0x146
00000000`042bc7c0 00000000`5bd2bd17 MSHTML!CHtmCrlfParseCtx::AddText+0x24
00000000`042bc800 00000000`5bd7daa5 MSHTML!CHtmParse::ParseText+0x349
00000000`042bc9c0 00000000`5bd2d36e MSHTML!CHtmParse::ParseToken+0x2d2
00000000`042bca00 00000000`5bd20980 MSHTML!CHtmPost::ProcessTokens+0x49b
00000000`042bcd00 00000000`5bd20f7b MSHTML!CHtmPost::Exec+0x274
00000000`042bcf10 00000000`5bd20e81 MSHTML!CHtmPost::Run+0x5a
00000000`042bcf50 00000000`5bd206eb MSHTML!PostManExecute+0x1ae
00000000`042bcfa0 00000000`5bd1f4e0 MSHTML!CDwnChan::OnMethodCall+0x1b
00000000`042bcfd0 00000000`5bdeb468 MSHTML!GlobalWndOnMethodCall+0x18b
00000000`042bd060 00000000`771d9bd1 MSHTML!GlobalWndProc+0x36c
00000000`042bd0e0 00000000`771d98da USER32!UserCallWinProcCheckWow+0x1ad
00000000`042bd1a0 000007fe`ebfdaf5e USER32!DispatchMessageWorker+0x3b5
00000000`042bd220 000007fe`ebf87754 IEFRAME!CTabWindow::_TabWindowThreadProc+0x9c1
00000000`042bf680 00000000`76d153b3 IEFRAME!LCIETab_ThreadProc+0x39f
00000000`042bf820 000007fe`ebf68dcb iertutil!CIsoScope::RegisterThread+0x10f
00000000`042bf850 00000000`76bf652d IEFRAME!Detour_DefWindowProcA+0x97
00000000`042bf890 00000000`772ec521 kernel32!BaseThreadInitThunk+0xd
00000000`042bf8c0 00000000`00000000 ntdll!RtlUserThreadStart+0x1d
After restarting Internet Explorer and the debugger (leading different memory layout), I set a breakpoint at the address of the faulting instruction.
0:005> g
Breakpoint 0 hit
MSHTML!CHtmSpaceParseCtx::AddNonspaces+0x134:
00000000`69f0aff5 410fb70c4c      movzx   ecx,word ptr [r12+rcx*2] ds:00000002`0421cb2e=????
r12 is the base address of a structure. The instruction reads a word value from the structure using a delta value that is RCX*2. In our case, RCX is 0`ffffffff which cannot be a valid delta value as the size of the memory region of the structure is only 4000 bytes as seen below. Therefore by executing the instruction it reads out of the bounds of the structure, that is likely an invalid memory address.
0:005> !vprot r12
BaseAddress:       000000000421c000
AllocationBase:    0000000004020000
AllocationProtect: 00000004  PAGE_READWRITE
RegionSize:        0000000000004000
State:             00001000  MEM_COMMIT
Protect:           00000004  PAGE_READWRITE
Type:              00020000  MEM_PRIVATE
By allocating memory at the address of the invalid read we can execute the instruction without causing exception.
MSHTML!CHtmSpaceParseCtx::AddNonspaces+0x134:
00000000`69f0aff5 410fb70c4c      movzx   ecx,word ptr [r12+rcx*2] ds:00000002`0421cb2e=????
0:005> .dvalloc /b 2`0421c000 1000
Allocated d000 bytes starting at 00000002`04210000
As seen below, movzx now tries to access to a valid memory region recently allocated by .dvalloc, filled with zeroes. If we let the program execute now it doesn't crash. Even if we refresh the SVG file it doesn't cause a read access violation because now the memory movzx tries to access is a valid address in the virtual address space.
0:005> r
rax=0000000000000000 rbx=0000000000000000 rcx=00000000ffffffff
rdx=0000000000000000 rsi=0000000000000000 rdi=000000000033bd50
rip=0000000069f0aff5 rsp=000000000421ca90 rbp=000000000df58d90
 r8=0000000000000000  r9=000000000e03fee0 r10=000000000dfe4401
r11=0000000000000001 r12=000000000421cb30 r13=0000000000000000
r14=000000000df58d90 r15=0000000000000001
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
MSHTML!CHtmSpaceParseCtx::AddNonspaces+0x134:
00000000`69f0aff5 410fb70c4c      movzx   ecx,word ptr [r12+rcx*2] ds:00000002`0421cb2e=0000
When the instruction is executed it reads the tainted word value to ECX. As seen below, the word value is copied to a second structure. At this point the tainted data is copied to a second structure.
0:005> p
MSHTML!CHtmSpaceParseCtx::AddNonspaces+0x13c:
00000000`69f0affd 66894f7c        mov     word ptr [rdi+7Ch],cx ds:00000000`0033bdcc=0020
Using the PoC, later on the execution the program doesn't read the tainted data from the second structure that is verified by processor breakpoint.

The tainted word value was zero as .dvalloc filled the memory with zeroes. Since we know the address of the word value we can try all the 65536 possibilities and watch what happens when processing the different word values. To achieve this I wrote a wrapper HTML which calls the SVG file periodically. Also, I set up the below commands which does the job automatically, so in every iteration we provide different tainted input in range of 0 to 65535.
0:005> bc 0
0:005> r $t0 = 0;
0:005> bp 69f0aff5 "j @ecx=ffffffff 'ew 2`0421cb2e @$t0; r $t0 = @$t0 + 1; .if (@$t0 <= 0xffff) { gc }'; gc"
0:005> g
When the command completed I concluded that we tried all the word values but neither of them seem to trigger unusual code paths using the PoC.

Further analyzing the data flow there is a possible place when the tainted data could be used to make decision on the execution flow, and it's the following.
MSHTML!CHtmSpaceParseCtx::AddNonspaces+0x87:
00000000`69f4823e 0fb74f7c        movzx   ecx,word ptr [rdi+7Ch]
00000000`69f48242 6685c9          test    cx,cx
00000000`69f48245 0f8511b8d2ff    jne     MSHTML!CHtmSpaceParseCtx::AddNonspaces+0x90 (00000000`69c73a5c)
If this code can be reached when [rdi+7c] is the tainted data that might have consequence due to the complexity of the code paths in this function. I however was unable to reach this code path in my experience.

The address of invalid read is predictable by the attacker as it's always 2 x 0`ffffffff plus something which can be filled with attacker controlled data. But this doesn't seem to be relevant as Microsoft just confirmed that they had finished their investigation into this crash and as they say "[we] can confirm that this crash is caused by a read AV which is not exploitable." I believe them and I no longer investigate this but will focus on other crashes, but thought to share this.

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

October 8, 2012

Flash Player Enables Sandbox via Global Pointer

The Windows version of Adobe Flash Player in Firefox has protected mode functionality. The concept of this mode is similar to the sandbox concept in Chrome: there is a broker process and a renderer process. The renderer process is running in low-integrity mode, and communicating with the broker process. The broker process is to give permission for legitimate system change but to reject questionable request made by the renderer process. The basic idea is the flash file is loaded and executed by the renderer process so, any non-legitimate invocation occurs by a vulnerability it will be rejected by the broker process.

Possible ways to circumvent the protection provided by protected mode include the followings. Corrupt the borkerer process to accept non-legitimate request from the renderer process. Or find Windows vulnerability to escalate right in low-integrity (i.e. renderer) process. Since the previous implementation when we didn't have the protected mode functionality is still available in the Flash Player it sounds to be logical by the attacker to disable protected mode functionality so the flash file is loaded and executed without sandbox protection. I'm exploring the latter further.

I examined NPSWF32_11_4_402_278.dll (11,264,432 bytes) file that is used in Firefox. In my test environment the DLL was located in C:\Windows\SysWOW64\Macromed\Flash folder. This file has the logic to decide if the flash file should be loaded and executed with or without sandbox protection. Flash Player uses a global pointer variable at fixed address (RVA: 0a2be70h) to store if sandbox mode should be used (1/0: with/without sandbox).

The value via the global pointer set to 1 by default (sandbox enabled) but it can be overwritten by adding ProtectedMode = 0 line to mms.cfg file that is located in the folder mentioned above.

Both the value and the page of the global pointer have the memory protection flags of read and write and since the global pointer is at a fixed address it's possible to overwrite it without changing memory protection flags or guessing the address.

Even though there is a need for a vulnerability to overwrite the global pointer table from security point of view it would be logical to discard the fixed address method and the writable memory protection flag. Using random address with read only page (apply writable flag when it's needed) would be more appropriate.

Here come some research artifacts.

Hijacking global pointer in Windbg. Higlighted dword is our new protected mode flag that is disabled.
0:000> sxe ld:NPSWF32_11_4_402_278
0:000> g
ModLoad: 59fe0000 5abbb000   C:\Windows\SysWOW64\Macromed\Flash\NPSWF32_11_4_402_278.dll
eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=fffdd000 edi=0059bfcc
eip=7784fc42 esp=0059bea0 ebp=0059bef4 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
ntdll!NtMapViewOfSection+0x12:
7784fc42 83c404          add     esp,4
0:000> db 59fe0000 +a2be70
5aa0be70  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
5aa0be80  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
5aa0be90  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
5aa0bea0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
5aa0beb0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
5aa0bec0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
5aa0bed0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
5aa0bee0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0:000> .dvalloc 1000
Allocated 1000 bytes starting at 00730000
0:000> db 00730000
00730000  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00730010  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00730020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00730030  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00730040  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00730050  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00730060  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00730070  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0:000> ed 59fe0000 +a2be70 00730000
0:000> g
Setting up protected mode flag below.
56245e56 807dff00        cmp     byte ptr [ebp-1],0
56245e5a 7406            je      NPSWF32_11_4_402_278!NP_SetNPAPIHostProxy+0x168c (56245e62)
56245e5c 8b45f8          mov     eax,dword ptr [ebp-8]
56245e5f c60001          mov     byte ptr [eax],1
56245e62 8bcb            mov     ecx,ebx
Checking if protected mode flag is set below.
56245e6f 8b75f8          mov     esi,dword ptr [ebp-8]
56245e72 59              pop     ecx
56245e73 803e00          cmp     byte ptr [esi],0
56245e76 0f8411010000    je      NPSWF32_11_4_402_278!NP_SetNPAPIHostProxy+0x17b7 (56245f8d) [br=1]
Highlighted code path of initialization with protected mode disabled below.
562460b0 e8dffeffff      call    NPSWF32_11_4_402_278!NP_SetNPAPIHostProxy+0x17be (56245f94)
562460b5 84c0            test    al,al
562460b7 7414            je      NPSWF32_11_4_402_278!NP_Initialize+0x57 (562460cd) [br=1]
562460b9 56              push    esi
562460ba e83bf4ffff      call    NPSWF32_11_4_402_278!NP_SetNPAPIHostProxy+0xd24 (562454fa)
562460bf 59              pop     ecx
562460c0 84c0            test    al,al
562460c2 7404            je      NPSWF32_11_4_402_278!NP_Initialize+0x52 (562460c8)
562460c4 33c0            xor     eax,eax
562460c6 eb0a            jmp     NPSWF32_11_4_402_278!NP_Initialize+0x5c (562460d2)
562460c8 33c0            xor     eax,eax
562460ca 40              inc     eax
562460cb eb05            jmp     NPSWF32_11_4_402_278!NP_Initialize+0x5c (562460d2)
562460cd e83595ffff      call    NPSWF32_11_4_402_278!unuse_netscape_plugin_Plugin+0x11de9 (5623f607)
562460d2 5e              pop     esi
562460d3 c20400          ret     4
I'd like to make it clear this is not a software vulnerability.

September 9, 2012

Using Windbg as Hex Editor

This blog post discusses the idea to use Windbg as hex editor. It consists of the followings: the advantage of using this approach, and the initial steps to getting started. Possible risks are discussed at the end of the post.

I often use hex editor to examine binaries. Even though some of them have rich feature set built-in sometimes there is a need to use special functionality that goes beyond the capability of any hex editor. Some hex editors have plugin interface so for them it's possible to write special functionality that accounts people needs. Although this is true, I have some plugin commands already written for Windbg, and there should not be a reason to code them again to work with some hex-editor, too. That would mean I should maintain both code base in the future which is not what I want.

In the recent days, I was thinking about what if I give it a try to tentatively use Windbg as a static hex editor. I was thinking about that I could use my plugin commands, and many built-in Windbg commands. It sounds very good because those commands are powerful. My second thought was if there was anything that I can achieve in hex editor but cannot in Windbg. I can't name anything so far. Beyond that, I don't even miss the graphical interface.

To use Windbg as hex editor the first things that needs to be done is to open an application (say notepad.exe) to get Windbg prompt. In the further steps, there is nothing to do with the application. Once the prompt is given it waits for command to execute. .dvalloc command is used to allocate memory. .readmem command is to copy the the content of the file to the allocated memory area. There is a script called LoadFile.wds [SkyDrive] that does the job so it's enough to execute this with the correct parameters. Below is the script.
$$ Load file to memory.
$$ 
$$ It reads binary data from the specified file and copies the data to the pre-allocated memory area.
$$
$$ Example Usage: $$>a< e:/LoadFile.wds e:/test.exe 1000
$$
$$ Created: 8/September/2012

.if ( ${/d:$arg1} & ${/d:$arg2} )
{
  $$ Get the 5th token of .dvalloc (i.e Address)
  .foreach /pS 5 (Address {.dvalloc ${$arg2}})
  {
    .readmem ${$arg1} Address L?${$arg2}
    db Address L180
  }
}
.else
{
  .echo "Usage     : LoadFile.wds <file> <size>";
  .echo "Parameters: <file> - File to be loaded"
  .echo "            <size> - Size of the file"
  .echo "Example   : $$>a< e:/LoadFile.wds e:/test.exe 1000";
}
The following demonstrates when a file being loaded using LoadFile.wds script.
0:000> $$>a< c:/work/LoadFile.wds c:/work/test.exe 1800
Reading 1800 bytes...
007e0000  4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00  MZ..............
007e0010  b8 00 00 00 00 00 00 00-40 00 00 00 00 00 00 00  ........@.......
007e0020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
007e0030  00 00 00 00 00 00 00 00-00 00 00 00 f0 00 00 00  ................
007e0040  0e 1f ba 0e 00 b4 09 cd-21 b8 01 4c cd 21 54 68  ........!..L.!Th
007e0050  69 73 20 70 72 6f 67 72-61 6d 20 63 61 6e 6e 6f  is program canno
007e0060  74 20 62 65 20 72 75 6e-20 69 6e 20 44 4f 53 20  t be run in DOS 
007e0070  6d 6f 64 65 2e 0d 0d 0a-24 00 00 00 00 00 00 00  mode....$.......
007e0080  f1 3e 51 d1 b5 5f 3f 82-b5 5f 3f 82 b5 5f 3f 82  .>Q.._?.._?.._?.
007e0090  ae c2 a3 82 b6 5f 3f 82-ae c2 a1 82 b4 5f 3f 82  ....._?......_?.
007e00a0  ae c2 95 82 a6 5f 3f 82-ae c2 94 82 b7 5f 3f 82  ....._?......_?.
007e00b0  bc 27 ac 82 b7 5f 3f 82-b5 5f 3e 82 9c 5f 3f 82  .'..._?.._>.._?.
007e00c0  ae c2 90 82 b7 5f 3f 82-ae c2 a2 82 b4 5f 3f 82  ....._?......_?.
007e00d0  52 69 63 68 b5 5f 3f 82-00 00 00 00 00 00 00 00  Rich._?.........
007e00e0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
007e00f0  50 45 00 00 4c 01 04 00-6f c2 38 50 00 00 00 00  PE..L...o.8P....
007e0100  00 00 00 00 e0 00 03 01-0b 01 0a 00 00 0a 00 00  ................
007e0110  00 0a 00 00 00 00 00 00-df 12 00 00 00 10 00 00  ................
007e0120  00 20 00 00 00 00 40 00-00 10 00 00 00 02 00 00  . ....@.........
007e0130  05 00 01 00 00 00 00 00-05 00 01 00 00 00 00 00  ................
007e0140  00 50 00 00 00 04 00 00-03 74 00 00 03 00 00 81  .P.......t......
007e0150  00 00 10 00 00 10 00 00-00 00 10 00 00 10 00 00  ................
007e0160  00 00 00 00 10 00 00 00-00 00 00 00 00 00 00 00  ................
007e0170  24 22 00 00 3c 00 00 00-00 40 00 00 b4 01 00 00  $"..<....@......
Important safety note is that the script uses .dvalloc command to allocate the memory for the data. As a consequence, the memory area has the protection flag of PAGE_EXECUTE_READWRITE. Therefore code execution from the memory page is possible even though it requires series of mistakes to make.

September 2, 2012

Timeline for Fix of an Arbitrary Address Write Bug

In today's post I'm writing about an arbitrary address write bug in Flash Player in Firefox (CVE-2012-4171) that is now fixed.

The issue was reported to Mozilla, but the fix made by Adobe as it turned out it was a bug in the Flash Player. The bug was tracked in Mozilla's bug tracking system (BugZilla) here [still restricted at the time of writing].

To trigger this vulnerability there is no requirement to use malformed data in any way. All the constructs can be legitimate. The PoC demonstrates when the security dialog box is shown it is possible to navigate to an other page that pops up another security dialog while the first dialog is still visible. When the second dialog is OK'd Flash Player crashes.

Below code snippet demonstrates the vulnerable code path.
mov     ecx,dword ptr [esi+4]           *(1)
<STRIPPED>
mov     esi,ecx
add     dword ptr [esi+1B8h],0FFFFFFFFh *(2)
At line (1) [ESI+4] is an address in a valid heap region. The heap can be arranged to contain user controlled data. Therefore, at line (2) an attacker can write arbitrary address using add instruction. Due to depending on add instruction the attacker has limited control what data to write.

Below is the timeline for the fix.
Days Date Channel Action
0 days 25-Jan-2010 E-mailReport sent to Mozilla.
0 days 25-Jan-2010 E-mailMozilla confirms the behavior described in the report. A case opened in the bug tracking database (BugZilla).
731 days 26-Jan-2012 BugZillaA Mozilla engineer says he can reproduce the issue with Firefox 9 on Windows 7 but cannot reproduce with nightly build.
731 days 26-Jan-2012 BugZilla An Adobe engineer says they will triage this issue ASAP.
749 days 13-Feb-2012 BugZilla A Mozilla engineer asks for status update from Adobe.
749 days 13-Feb-2012 BugZilla An Adobe engineer confirms he cannot reproduce the crash.
749 days 13-Feb-2012 BugZilla An Adobe engineer confirms he can reproduce the crash by experimenting with waiting times before clicking "OK" on the prompt.
749 days 13-Feb-2012 BugZilla Reporter recommends to switch off plugin-container introduced since the report to get consistent crash state.
779 days 14-March-2012 E-mailReporter asks for status update, and points out the existing testcase reliably crashes Firefox when plugin-container disabled.
779 days 14-March-2012 E-mailMozilla confirm there is no other update on the case, than seen in BugZilla. Mozilla asks to put additional information about crashes.
780 days 15-March-2012 BugZilla Reporter adds additional information about crashes.
780 days 15-March-2012 E-mailReporter asks for an estimation of the date of the fix.
780 days 15-March-2012 E-mailMozilla confirm they don't have an estimation. They say to have to see what the developers can do with it in the short term.
780 days 15-March-2012 E-mailIn response to an earlier email Mozilla say "flash bugs are often slow to be fixed".
805 days 9-Apr-2012 E-mailReporter decided to share crash information with CERT, and asked if they can help motivating the vendor to issue a patch.
805 days 9-Apr-2012 E-mailCERT confirm they have spoken with the vendor.
806 days 10-Apr-2012 BugZilla Mozilla say they effectively mitigated this issue with out of process plugins and they require a testcase which crashes Firefox in an exploitable manner.
806 days 10-Apr-2012 BugZilla Reporter responses that existing testcase crashes Firefox in 1/10 ratio on reporter side. With plugin-container disabled it crashes Firefox in 10/10 ratio.
807 days 11-Apr-2012 BugZilla An Adobe engineer confirms they cannot reproduce the issue.
808 days 12-Apr-2012 BugZilla Reporter recommends to disable plugin-container to get a consistent crash state. Reporter also highlights plugin container is just a mitigation that could be bypassed.
808 days 12-Apr-2012 BugZilla A Mozilla Security Researcher asks how the plugin-container could be bypassed.
808 days 12-Apr-2012 BugZilla Reporter highlights attack surfaces but he also states discussing how to bypass mitigation is out of the scope of this bug.
814 days 18-Apr-2012 BugZilla Adobe say they have a good set of test cases for this issue now, and this will be fixed in the following release.
861 days 4-June-2012 BugZilla Adobe confirm it is fixed in 11.4.400.128. [This seems to be an internal build]
939 days 21-Aug-2012 WebFix released in 11.4.402.265. To see Adobe's Security Bulletin click here.
N/A 27-Aug-2012 BugZilla Adobe confirm 11.4.402.265 is released on 21st of August and mark the bug as fixed.
N/A 29-Aug-2012 E-mailReporter checks Adobe's Security Bulletin and realizes this bug is not mentioned in it. Asks Adobe regarding CVE number.
N/A 30-Aug-2012 E-mailAdobe confirm the Security Bulletin has been updated with the CVE number.
This timeline contains the technically important communications. If you are aware of that I missed something that you think is important to mention, please let me know and I'll update this page.

Thanks for those who involved from Mozilla, Adobe and CERT.

August 28, 2012

Read-Before-Write: An Example for False Positive Detection

During the Bank Holiday weekend I was working on testing and stabilizing one of my Windbg extension commands which detects read-before-write bugs. According to the test result some detection appeared to be false positive, and in this post I discuss a case that is interesting to me.

Instead of analyzing the test result in the Windbg log file I decided to do it in IDA. I took the errors from the Windbg log and simply highlighted the instructions causing read-before-write in IDA. The highlight idea was borrowed from team ZDI (thanks).

Below is the code snippet wherein an error detected that appears to be a false positive.
There are two questions need answering:
  • Why is it a false positive detection?
  • Why the detection was mistakenly issued?
Answering why is it a false positive question is very straightforward. void* is pushed onto the stack that is a parameter for delete(). delete() uses cdecl calling convention so it doesn't clean-up void* on the stack but pop ecx in the red line does. pop ecx reads void*. The memory address void* read from has been written by push instruction. So the red line cannot be a read-before-write access.

Answering the second question: why the detection was mistakenly issued is a bit more complex and requires knowledge how my Windbg plugin extension works - read the principles here if you like. When push [ebp+var_78] is executed it does the followings. First, it reads var_78 local variable, then it writes (pushes) var_78 onto the stack. The address of the read access and the address of the write access are both stack memory addresses. In this example, the protection flags were set on stack memory region by the Windbg command extension. As mentioned above when push is executed read access occurs first. We handle this by removing the protection flags and let the push execute. Since the memory protection flags removed push didn't have a chance to cause a write access exception, therefore the address is not added to the list of written addresses. Later on the execution, pop causes a read access violation and the plugin checks if the address has been written. Since the address is not on the list of written addresses it issues a read-before-write notification, mistakenly. The answer in short is because we didn't catch the write access of push.

The current implementation wrongly assumes that an instruction could cause either read or write access. As seen above, in fact, an instruction could cause both read and write accesses. A possible fix would be to deal with the situation of multiple accesses. However, I don't think it's a priority to implement at the moment because it's possible to do a post process on the output log to mark or to remove these situations when showed in IDA.

August 20, 2012

Research notes of finding Stale Pointer bugs

Keywords: use-after-free, dangling pointer, stale pointer, invalid pointer dereference, double free, deleted object

In 2009 Mozilla fixed one of the vulnerability (CVE-2009-2467) I reported them. It had allowed to reference freed object leading to code execution. Some time after this in 2010 I got interested researching this class of security bugs. I pursued dynamic approach to discover them but I wanted different approach than feeding the application with fuzzed data. It was also a requirement to discover these problems in binaries without having source code or debug information available.

I thought it would be nice to make it visible if a pointer is dangling. I knew detecting the pointer that is dangling could come with lots of false positives because some of them cannot be referenced from the execution flow.

Anyway, I came up with the following train of thought. malloc() is used to allocate a region of memory on the heap. The pointer referencing to the region of memory can be on the stack or on another region of the heap. So we need to handle the situation in a different way if a pointer is a dangling pointer on the stack or on the heap.

I kept working on this approach and thought we need to maintain a structure what region of memory was allocated and freed. I thought when free() is called we could check if there is a reference to the freed memory. Here is a skeleton of the approach I draw back in 2010 - it's unprofessional and not so important so you might wanna continue reading instead... :)

The solution above wouldn't have worked in practice however. The conceptual problem here is if there is a reference to a freed object it doesn't mean the code would use the pointer is reachable. When a region of memory is freed the reference might exist to the freed object even if you explicitly set it to NULL. This is because the compiler optimizes this out if it cannot be reached. Another conceptual problem is the original idea itself that is we depend on the check for the references only when free() is called.

I had discussed this idea to people how could this be improved but concluded none of the solutions would be practically applicable. Possible improvements involve timing, and applied static analysis. Static analysis in dynamic approach might be an area to explore further but this would require significant research effort and showed only a little benefit that time.

The low-level constructs are complex so I knew we could detect something that indicates the presence of dangling pointer because in a complex environment it's so big the playground. I suspended working on this for a long time, however, with the fact in my mind that I have a solution that show the sign of working. It was just not optimal enough to use it in practice.

Couple of weeks ago I started working on a debugger extension to place data breakpoint on arbitrary size of the memory. I had a huge success and already built two functionalities on it - both of them detect possible security problems. Thought why not give it a try to explore the old dangling pointer project further involving this new approach.

From the previous posts, you might know that it's possible to track data access, and to determine the kind of the access that is either read or write access. By applying hook on malloc() and on free() we can maintain a list of allocated and freed memory blocks. When there is a read data access to a pointer to freed memory we can issue a notification: pointer to freed memory has been read.

Here is an isolated example code that reads pointer to freed memory.
int *read_freed_ptr(void)
{
    int *ptr = (int *)malloc(sizeof(int));

    free(ptr);

    // read pointer to freed memory
    return ptr;
}
When a freed pointer is read it might not cause a crash later on the execution but definitely could do if the pointer is dereferenced. It is possible, for example, a lot of fuzzing cases cause the application to read freed pointer but the bugs remain undetected because they are not dereferenced. I'm particularly interested researching this approach further on JIT emitted code.

Anyway, here is the assembly code for the above C code. I highlighted the area when the pointer to the freed memory is read.
00401000 55                   push        ebp  
00401001 8B EC                mov         ebp,esp  
00401003 51                   push        ecx  
00401004 6A 04                push        4  
00401006 FF 15 A4 20 40 00    call        dword ptr [__imp__malloc (4020A4h)]  
0040100C 83 C4 04             add         esp,4  
0040100F 89 45 FC             mov         dword ptr [ptr],eax  
00401012 8B 45 FC             mov         eax,dword ptr [ptr]  
00401015 50                   push        eax  
00401016 FF 15 9C 20 40 00    call        dword ptr [__imp__free (40209Ch)]  
0040101C 83 C4 04             add         esp,4  
0040101F 8B 45 FC             mov         eax,dword ptr [ptr]  
00401022 8B E5                mov         esp,ebp  
00401024 5D                   pop         ebp  
00401025 C3                   ret
Detecting read of pointer to freed memory is possible and straightforward task to do prototype implementation.

--Attila Suszter (@reon_wi)

August 13, 2012

An approach to detect signedness conversion

Here come the details of the code that is built on the top of the functionality involving detecting uninitialized read access to stack memory.

With the current implementation it's possible to make it visible how certain integers on the stack are being treated: whether signed or unsigned. This means if there is an integer that is accessed from multiple locations in the execution flow and we successfully determined how the integer was treated at each location in the execution flow we are able to tell if the integer is treated both signed and unsigned.

When there is a read access to the stack memory (a local variable read) and the instruction causing the read access exception is CMP we read the next instruction. If the next instruction is one of the followings the comparison is signed because these jumps based on signed comparisons: JG, JGE, JL, JLE, JNG, JNGE, JNL, JNLE. If the next instruction is one of the following the comparison is unsigned: JA, JAE, JB, JBE, JNA, JNAE, JNB, JNBE. Below is an example.

Signedness conversion is not a vulnerability but easily could be. For example, when the developer eliminates a signed/unsigned mismatch compiler warning in an if() condition by using explicit typecast. This could lead to the situation the program works normally but when the variable contains an unexpected value the execution continues on a different code path than it should due to the signedness conversion.

The signedness conversion could happen implicitly, too.

I wrote a test that you can see below to show how the code works.
// TestSignednessConversion.cpp : Example program for signedness conversion.
// Compile with optimization disabled.

#include "stdafx.h"

void set(int* value, int* value2)
{
    if (*value > 2)
    {
        *value = 2;
    }

    if (*value2 < 2)
    {
        *value2 = 2;
    }
}

void set2(unsigned int* value)
{
    if (*value > 2)
    {
        *value = 2;
    }
}

void set3(int* value2)
{
    if (*value2 < 0xffffffffU)
    {
        *value2 = 2;
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    int          value  = 3; // Treated both as signed and as unsigned (explicit cast)
    int          value2 = 1; // Treated both as signed and as unsigned (implicit)
    unsigned int value3 = 3; // Treated as unsigned only

    // value treated as signed
    // value2 treated as signed
    set(&value, &value2);

    // value treated as unsigned (explicit cast)
    set2((unsigned int*)&value);

    // value2 treated as unsigned (implicit)
    set3(&value2);

    // value3 treated as unsigned
    set2(&value3);

    return 0;
}
And here is the corresponding log of the program that is a Windbg extension. The log is verbose showing both read and write stack memory accesses. I highlighted the parts when the signedness of the comparison determined so you can match the parts to the source code above. You can also see when a variable is treated both as signed and as unsigned. U stands for unsigned comparison, S stands for signed comparison.
0:000> g wmain
eax=002b1a40 ebx=00000000 ecx=5d47471c edx=00000000 esi=00000001 edi=00403374
eip=00401070 esp=0018ff48 ebp=0018ff88 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
TestSignednessConversion!wmain:
00401070 55              push    ebp
0:000> l-t
Source options are 0:
    None
0:000> sxi av
0:000> !load c:\work\ext
0:000> !vprot esp
BaseAddress:       0018f000
AllocationBase:    00090000
AllocationProtect: 00000004  PAGE_READWRITE
RegionSize:        00001000
State:             00001000  MEM_COMMIT
Protect:           00000004  PAGE_READWRITE
Type:              00020000  MEM_PRIVATE
0:000> !region 56 18f000 1000
[Data Access] EIP=00401070 Data=0018ff44 W -
[Data Access] 00401070 55              push    ebp
[Data Access] EIP=00401076 Data=0018ff40 W -
[Data Access] 00401076 c745fc03000000  mov     dword ptr [ebp-4],3
[Data Access] EIP=0040107d Data=0018ff38 W -
[Data Access] 0040107d c745f401000000  mov     dword ptr [ebp-0Ch],1
[Data Access] EIP=00401084 Data=0018ff3c W -
[Data Access] 00401084 c745f803000000  mov     dword ptr [ebp-8],3
[Data Access] EIP=0040108e Data=0018ff34 W -
[Data Access] 0040108e 50              push    eax
[Data Access] EIP=00401092 Data=0018ff30 W -
[Data Access] 00401092 51              push    ecx
[Data Access] EIP=00401093 Data=0018ff2c W -
[Data Access] 00401093 e868ffffff      call    TestSignednessConversion!set (00401000)
[Data Access] EIP=00401000 Data=0018ff28 W -
[Data Access] 00401000 55              push    ebp
[Data Access] Value at 0018ff30 is read.
[Data Access] EIP=00401003 Data=0018ff30 R -
[Data Access] 00401003 8b4508          mov     eax,dword ptr [ebp+8]
[Data Access] Value at 0018ff40 is read.
[Data Access] EIP=00401006 Data=0018ff40 R S
[Data Access] 00401006 833802          cmp     dword ptr [eax],2
[Data Access] Value at 0018ff30 is read.
[Data Access] EIP=0040100b Data=0018ff30 R -
[Data Access] 0040100b 8b4d08          mov     ecx,dword ptr [ebp+8]
[Data Access] EIP=0040100e Data=0018ff40 W -
[Data Access] 0040100e c70102000000    mov     dword ptr [ecx],2
[Data Access] Value at 0018ff34 is read.
[Data Access] EIP=00401014 Data=0018ff34 R -
[Data Access] 00401014 8b550c          mov     edx,dword ptr [ebp+0Ch]
[Data Access] Value at 0018ff38 is read.
[Data Access] EIP=00401017 Data=0018ff38 R S
[Data Access] 00401017 833a02          cmp     dword ptr [edx],2
[Data Access] Value at 0018ff34 is read.
[Data Access] EIP=0040101c Data=0018ff34 R -
[Data Access] 0040101c 8b450c          mov     eax,dword ptr [ebp+0Ch]
[Data Access] EIP=0040101f Data=0018ff38 W -
[Data Access] 0040101f c70002000000    mov     dword ptr [eax],2
[Data Access] Value at 0018ff28 is read.
[Data Access] EIP=00401025 Data=0018ff28 R -
[Data Access] 00401025 5d              pop     ebp
[Data Access] Value at 0018ff2c is read.
[Data Access] EIP=00401026 Data=0018ff2c R -
[Data Access] 00401026 c3              ret
[Data Access] EIP=0040109e Data=0018ff34 W -
[Data Access] 0040109e 52              push    edx
[Data Access] EIP=0040109f Data=0018ff30 W -
[Data Access] 0040109f e88cffffff      call    TestSignednessConversion!set2 (00401030)
[Data Access] EIP=00401030 Data=0018ff2c W -
[Data Access] 00401030 55              push    ebp
[Data Access] Value at 0018ff34 is read.
[Data Access] EIP=00401033 Data=0018ff34 R -
[Data Access] 00401033 8b4508          mov     eax,dword ptr [ebp+8]
[Data Access] Value at 0018ff40 is treated both as signed and as unsigned. 
[Data Access] EIP=00401036 Data=0018ff40 R U
[Data Access] 00401036 833802          cmp     dword ptr [eax],2
[Data Access] Value at 0018ff2c is read.
[Data Access] EIP=00401044 Data=0018ff2c R -
[Data Access] 00401044 5d              pop     ebp
[Data Access] Value at 0018ff30 is read.
[Data Access] EIP=00401045 Data=0018ff30 R -
[Data Access] 00401045 c3              ret
[Data Access] EIP=004010aa Data=0018ff34 W -
[Data Access] 004010aa 50              push    eax
[Data Access] EIP=004010ab Data=0018ff30 W -
[Data Access] 004010ab e8a0ffffff      call    TestSignednessConversion!set3 (00401050)
[Data Access] EIP=00401050 Data=0018ff2c W -
[Data Access] 00401050 55              push    ebp
[Data Access] Value at 0018ff34 is read.
[Data Access] EIP=00401053 Data=0018ff34 R -
[Data Access] 00401053 8b4508          mov     eax,dword ptr [ebp+8]
[Data Access] Value at 0018ff38 is treated both as signed and as unsigned. 
[Data Access] EIP=00401056 Data=0018ff38 R U
[Data Access] 00401056 8338ff          cmp     dword ptr [eax],0FFFFFFFFh
[Data Access] Value at 0018ff34 is read.
[Data Access] EIP=0040105b Data=0018ff34 R -
[Data Access] 0040105b 8b4d08          mov     ecx,dword ptr [ebp+8]
[Data Access] EIP=0040105e Data=0018ff38 W -
[Data Access] 0040105e c70102000000    mov     dword ptr [ecx],2
[Data Access] Value at 0018ff2c is read.
[Data Access] EIP=00401064 Data=0018ff2c R -
[Data Access] 00401064 5d              pop     ebp
[Data Access] Value at 0018ff30 is read.
[Data Access] EIP=00401065 Data=0018ff30 R -
[Data Access] 00401065 c3              ret
[Data Access] EIP=004010b6 Data=0018ff34 W -
[Data Access] 004010b6 51              push    ecx
[Data Access] EIP=004010b7 Data=0018ff30 W -
[Data Access] 004010b7 e874ffffff      call    TestSignednessConversion!set2 (00401030)
[Data Access] EIP=00401030 Data=0018ff2c W -
[Data Access] 00401030 55              push    ebp
[Data Access] Value at 0018ff34 is read.
[Data Access] EIP=00401033 Data=0018ff34 R -
[Data Access] 00401033 8b4508          mov     eax,dword ptr [ebp+8]
[Data Access] Value at 0018ff3c is read.
[Data Access] EIP=00401036 Data=0018ff3c R U
[Data Access] 00401036 833802          cmp     dword ptr [eax],2
[Data Access] Value at 0018ff34 is read.
[Data Access] EIP=0040103b Data=0018ff34 R -
[Data Access] 0040103b 8b4d08          mov     ecx,dword ptr [ebp+8]
[Data Access] EIP=0040103e Data=0018ff3c W -
[Data Access] 0040103e c70102000000    mov     dword ptr [ecx],2
[Data Access] Value at 0018ff2c is read.
[Data Access] EIP=00401044 Data=0018ff2c R -
[Data Access] 00401044 5d              pop     ebp
[Data Access] Value at 0018ff30 is read.
[Data Access] EIP=00401045 Data=0018ff30 R -
[Data Access] 00401045 c3              ret
[Data Access] Value at 0018ff44 is read.
[Data Access] EIP=004010c3 Data=0018ff44 R -
[Data Access] 004010c3 5d              pop     ebp
[Data Access] Value at 0018ff48 is read.
[Data Access] EIP=004010c4 Data=0018ff48 R -
[Data Access] 004010c4 c3              ret
Break reason: 00000010
In the log l-t is set to disable step to next source line and use step to next instruction instead. sxi av is used to let the event callback implementation handle the exception. 18f000 is stack base address 1000 is the size. 56 is to set some flags including verbose logging, etc.

August 8, 2012

Experiences with Signedness II

Data that is read from the memory can be treated as signed integer or unsigned integer. It's possible that at some stage of the execution the integer is treated as unsigned integer but other point of the execution it's treated as signed integer. When it comes to write code there could be circumstances when you might not be immediately aware how the integer is treated unless you take an extra care, for example, by looking at the compiled code. This is definitely an attack surface, and the root cause of lots of published vulnerabilities.

In case you want to see some examples what I mean, earlier last year, I wrote a little about experiences regarding signed/unsigned comparisons.

Some time ago, I started developing a Windbg plugin command that has a tracing functionality, and the ability to break in the debugger when a signed comparison is reached. However, if EIP is not in user defined range e.g. due to an API call, the program executes normally. When EIP is in the user defined range again the program resumes tracing.

I was able to trace some function in a Visual C++ project, but it was needed to run l-t command beforehand to step by assembly instructions rather than source lines. Here is how to use Windbg in VS.

This plugin can be extended to work with other signed instructions than signed comparison ones. In addition, the plugin can be extended to execute the program until comparison is reached rather than to trace, in a similar way to the working of the ph command.

One possible area to explore further is to record how the data that is read from the memory is treated in point of signedness. Also, to detect any weak points to attack, or even to detect signedness conversions.

July 24, 2012

Detecting Read Access to Uninitialized Stack Memory

As I mentioned in the previous post I developed a program that is able to trace when the stack memory is being accessed. This time, I improved this program so if there is a read access to the portion of the stack memory that was not written before it issues a read-before-write notification. This happens when for example an uninitialized variable is being read.

Here is one of the erroneous C program I tested my test tool with. The C program reads uninitialized variable both in process and process2 functions from the CONTEXT structure. Highlighted the lines accessing uninitialized memory.
// TestRBW.cpp : Example file to test read-before-write bugs.
// Compile with optimization disabled.

#include "stdafx.h"
#include <string.h>

#define MAX 16

typedef struct _CONTEXT
{
    int arr[MAX];
    int a;
    int b;
    int c;
} CONTEXT;

void init(CONTEXT* ctx)
{
    memset(ctx->arr, 0, sizeof(ctx->arr[0]) * (MAX-1));
    ctx->a = 1;
}

void process(CONTEXT* ctx)

{
    int trash;

    for (int i = 0; i < MAX; i++)

    {
        trash = ctx->arr[i];
    }
}

void process2(CONTEXT* ctx)

{
    ctx->b = ctx->c;
}

void process3(int num)

{
    int trash;

    if (num)

        trash = num;
}

int _tmain(int argc, _TCHAR* argv[])

{
    CONTEXT ctx;

    // Erroneously initializes context. The last element of arr member remains unitialized.

    // b and c members remain uninitialized, too.
    init(&ctx);

    // Accesses to each element of the array. Read-before-write error should be reported in this function.

    process(&ctx);

    // Copies c to b but c is uninitialized. Read-before-write error should be reported in this function.

    process2(&ctx);

    // This contains no read-before-write bug.

    process3(ctx.a);
}
And here is the result of the test. In the log you can see both of the bugs were detected.
0:000> !region 18 18f000 1000
[Data Access] Data at 0018ff28 is read before it is written
[Data Access] EIP=00401044 Data=0018ff28 R
[Data Access] 00401044 8b048a          mov     eax,dword ptr [edx+ecx*4]
[Data Access] Data at 0018ff34 is read before it is written
[Data Access] EIP=00401059 Data=0018ff34 R
[Data Access] 00401059 8b5148          mov     edx,dword ptr [ecx+48h]
Break reason: 00000010
Looking the functions of the two EIPs. Highlighted the lines accessing uninitialized memory.
TestRBW!process:
00401020 55              push    ebp
00401021 8bec            mov     ebp,esp
00401023 83ec08          sub     esp,8
00401026 c745f800000000  mov     dword ptr [ebp-8],0
0040102d eb09            jmp     TestRBW!process+0x18 (00401038)
0040102f 8b45f8          mov     eax,dword ptr [ebp-8]
00401032 83c001          add     eax,1
00401035 8945f8          mov     dword ptr [ebp-8],eax
00401038 837df810        cmp     dword ptr [ebp-8],10h
0040103c 7d0e            jge     TestRBW!process+0x2c (0040104c)
0040103e 8b4df8          mov     ecx,dword ptr [ebp-8]
00401041 8b5508          mov     edx,dword ptr [ebp+8]
00401044 8b048a          mov     eax,dword ptr [edx+ecx*4]
00401047 8945fc          mov     dword ptr [ebp-4],eax
0040104a ebe3            jmp     TestRBW!process+0xf (0040102f)
0040104c 8be5            mov     esp,ebp
0040104e 5d              pop     ebp
0040104f c3              ret
TestRBW!process2:
00401050 55              push    ebp
00401051 8bec            mov     ebp,esp
00401053 8b4508          mov     eax,dword ptr [ebp+8]
00401056 8b4d08          mov     ecx,dword ptr [ebp+8]
00401059 8b5148          mov     edx,dword ptr [ecx+48h]
0040105c 895044          mov     dword ptr [eax+44h],edx
0040105f 5d              pop     ebp
00401060 c3              ret
I built this new functionality on the top of one mentioned in the previous post. The additions are the followings:
  • We trace both read and write memory accesses to the stack memory.
  • We maintain a structure to flag what memory addresses on the stack have been written.
  • Before the program starts to trace the memory it considers that memory addresses greater or equal than ESP have been written and maintain the structure according to this.
  • If a write memory access occurs we maintain the structure to flag the memory region has been written.
  • If an element is popped from the stack (read access to stack memory) we maintain the structure to remove addresses belongs to the unused portion of the stack memory.
  • If a read access memory occurs we read the structure and check if the memory at the address has been written. Giving notification according to results.

July 8, 2012

Tracing access to stack memory

In the previous post I wrote about a concept to intercept when arbitrary region of the memory is being accessed.

Below is an example output of the Windbg extension. I set it to monitor when the stack memory of the current thread (Address: 18d000 Size: 3000) is being accessed, and to break in when there is a call to other module (Break reason: 2).

In the log you can see the address where the memory access occurred and the instruction caused the memory access. Also, you can see the address of the data being accessed, and the type of the data access (R for read, W for write).

July 1, 2012

Data Access Breakpoint Using Memory Protection

When debugging data formats I sometimes think about how cool would be to have a debugger command that would allow me to place data access breakpoint on arbitrary region of the memory. Certainly, I can put hardware breakpoint on memory region with the restrictions that the size of the region is either 1, 2 or 4 bytes on IA32 architecture.

But what if I want to place a breakpoint on a data dump that has a size of greater than 4 bytes. For example, I want to do this to see what location within the region would be first accessed. Normally, there is no way to do this because of the lack of the support IA32 architecture provides. Software solution is possible but not implemented in debuggers such as in Ollydbg and in Windbg.

What you could do in these circumstances is to guess where the code parsing the data starts, and place a breakpoint on the execution flow, so when you break in the debugger you can trace the code from that point and watch the data flow. This is likely to be tedious process because you spend time stepping through code you are not interested. The other ideas tend to be even more tedious, and require prepration work (hooking etc.) We shouldn't forget that the reason we want to place breakpoint on memory region is usually to narrow down the scope of total debugging.

Some time ago I've seen how Armadillo protection uses debugge/debugger process to mark encrypted pages and when there is an access to the page it gets decrypted and executed on-the-fly. Also, EMET (Enhanced Mitigation Experience Toolkit) in the heap spray mitigation function uses to mark certain pages as unaccessible so the system cannot allocate memory to that address. This has inspired me how to write a plugin that allows me to place data breakpoint on arbitrary memory region. Yay!

Using VirtualProtect it's possible to disable all access to the memory page in the process address space. I however don't use VirtualProtect in the plugin but use VirtualProtectEx instead because the extended version allows me to disable all access to the memory in specified process rather than the current process.

The prototype has been developed as an extension command for Windbg. It requires two parameters describing the breakpoint: the address and the size of the region. When data is accessed within this region the we break in the debugger.

When the extension command runs it locates the base address of the memory page the specified region belongs to. Next, PAGE_NOACCESS protection flag is set for the memory page. This allows us to filter any access to the memory page via exception handling.

When the target runs in the debugger, and if there is an attempt to read from the protected page it results in an access violation. Due to this, the exception event callback implemented in the Windbg extension is executed. This callback verifies if the address of inaccessible data is within the region of the data breakpoint. If it is we restore the original protection flags for the page and break in the debugger - breakpoint hit. Otherwise, we restore the original protection flag to execute the single instruction previously generated the exception. Then we restore the protection flags do disable all access and continue to run the application until there is an attempt to read from the protected page resulting in an access violation.

The exception information is stored in EXCEPTION_RECORD structure which is available in the exception even callback function. Exception->ExceptionAddress is the address where the exception occurred. Exception->ExceptionInformation[1] is the address of the inaccessible data. More information on exception is available here.

Before I run the plugin command I also run sxn av so if access violation occurs we don't break in the debugger but get notification of the exception. Thus we let the event callback handle the exception.

When the memory page is too big - irrespectively of the breakpoint size - it might be possible it's being accessed too many times. In this case, the debugging tends to be extremely slow because of the frequent context switches between the target process and the debugger.

An enhancement of this plugin would be to let the execution continue when a data is accessed but to record each address being accessed so we can trace the memory access for certain memory regions.

June 18, 2012

Calling C# Code from Windbg Extension

It could be useful to write functions in C#, and to call some of those from Windbg plugin. Here is an approach how to achieve this.

Create a new project from Class Library template in Microsoft Visual C# 2010 Express.

Go to project properties an tick Register for COM interop off.

Enable ComVisible by editing AssemblyInfo.cs like below.
[assembly: ComVisible(true)]
Here is a simplified implementation to add.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace ExtensionUtils
{
    public interface IManagedInterface
    {
        int Func();
    } 
    public class Utils : IManagedInterface
    {
        public int Func()
        {
            return 1;
        }
    }
}
When compiling the code both ExtensionUtils.tlb and ExtensionUtils.dll have been created.

Open the Windbg plugin in Microsoft Visual C++ 2010 Express.

Add the following code that calls the C# function. But make sure to to copy ExtensionUtils.tlb and ExtensionUtils.dll files to directory that has been added to the (additional) include directories in the project settings.

#import "ExtensionUtils.tlb" named_guids 
[...] 
    HRESULT hRes; 
    CoInitialize(NULL); 
    ExtensionUtils::IManagedInterface *pManagedInterface = NULL; 
    hRes = CoCreateInstance(ExtensionUtils::CLSID_Utils, NULL, CLSCTX_INPROC_SERVER, 
     ExtensionUtils::IID_IManagedInterface, reinterpret_cast<void**> (&pManagedInterface));
    if (hRes == S_OK)
    {
        int ret = pManagedInterface->Func();
    } 
    CoUninitialize();
That's how I did.
  This blog is written and maintained by Attila Suszter. Read in Feed Reader.