Enhance the query and add more test cases

This commit is contained in:
luchua-bc
2020-10-14 03:45:47 +00:00
committed by Chris Smowton
parent 55af37312b
commit 5338332648
6 changed files with 129 additions and 49 deletions

View File

@@ -20,19 +20,36 @@ public class UnsafeAndroidAccess extends Activity {
}
});
String thisUrl = getIntent().getExtras().getString("url"); // dangerous remote input
//String thisUrl = getIntent().getStringExtra("url"); //An alternative way to load dangerous remote input
String thisUrl = getIntent().getExtras().getString("url"); // dangerous remote input from the intent's Bundle of extras
wv.loadUrl(thisUrl);
}
// GOOD: Have JavaScript and universal resource access disabled while taking
// remote user inputs
// BAD: Have both JavaScript and universal resource access enabled in webview while
// taking remote user inputs
{
WebView wv = (WebView) findViewById(R.id.my_webview);
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowUniversalAccessFromFileURLs(true);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
String thisUrl = getIntent().getStringExtra("url"); //dangerous remote input from intent extra
wv.loadUrl(thisUrl);
}
// GOOD: Have JavaScript and universal resource access disabled by default on modern Android (Jellybean+) while taking remote user inputs
{
WebView wv = (WebView) findViewById(-1);
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(false);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {

View File

@@ -2,9 +2,9 @@
<qhelp>
<overview>
<p>Android WebViews that allow loading urls controlled by external inputs and whose JavaScript interface is enabled are potentially vulnerable to cross-site scripting and sensitive resource disclosure attacks.</p>
<p>WebSettings of WebViews with setAllowFileAccessFromFileURLs or setAllowUniversalAccessFromFileURLs enabled must not load any untrusted web content.</p>
<p>Enabling this setting allows malicious scripts loaded in a file:// context to launch cross-site scripting attacks, either accessing arbitrary local files including WebView cookies, session tokens, private app data or even credentials used on arbitrary web sites.</p>
<p>Android WebViews that allow loading URLs controlled by external inputs and whose JavaScript interface is enabled are potentially vulnerable to cross-site scripting and sensitive resource disclosure attacks.</p>
<p>A <code>WebView</code> whose <code>WebSettings</code> object has <code>setAllowFileAccessFromFileURLs(true)</code> or <code>setAllowUniversalAccessFromFileURLs(true)</code> called must not load any untrusted web content.</p>
<p>Enabling these settings allows malicious scripts loaded in a <code>file://</code> context to launch cross-site scripting attacks, either accessing arbitrary local files including WebView cookies, session tokens, private app data or even credentials used on arbitrary web sites.</p>
<p>This query detects the following two scenarios:</p>
<ol>
<li>Vulnerability introduced by WebViews with JavaScript enabled and remote inputs allowed.</li>
@@ -13,11 +13,11 @@
</overview>
<recommendation>
<p>Only allow trusted web contents to be displayed in WebViews when JavaScript is enabled. And disallow universal resource access in WebSettings to reduce the attack surface.</p>
<p>Only allow trusted web content to be displayed in WebViews when JavaScript is enabled. Disallow universal resource access in WebSetting to reduce the attack surface .</p>
</recommendation>
<example>
<p>The following example shows both 'BAD' and 'GOOD' configurations. In the 'BAD' configuration, universal resource access is enabled and JavaScript is enabled while urls are loaded from externally controlled inputs. In the 'GOOD' configuration, JavaScript is disabled or only trusted web contents are allowed to be loaded.</p>
<p>The following example shows both 'BAD' and 'GOOD' configurations. In the 'BAD' configuration, setting is enabled and JavaScript is enabled while URLs are loaded from externally controlled inputs. In the 'GOOD' configuration, JavaScript is disabled or only trusted web content is allowed to be loaded.</p>
<sample src="UnsafeAndroidAccess.java" />
</example>

View File

@@ -1,5 +1,5 @@
/**
* @name Unsafe resource loading in Android webview
* @name Unsafe resource fetching in Android webview
* @description JavaScript rendered inside WebViews can access any protected application file and web resource from any origin
* @kind path-problem
* @tags security
@@ -13,10 +13,10 @@ import semmle.code.java.frameworks.android.WebView
import semmle.code.java.dataflow.FlowSources
/**
* Allow universal access methods in the WebSettings class
* Methods allowing any-local-file and universal access in the WebSettings class
*/
class AllowUniversalAccessMethod extends Method {
AllowUniversalAccessMethod() {
class CrossOriginAccessMethod extends Method {
CrossOriginAccessMethod() {
this.getDeclaringType() instanceof TypeWebSettings and
(
this.hasName("setAllowUniversalAccessFromFileURLs") or
@@ -36,22 +36,22 @@ class AllowJavaScriptMethod extends Method {
}
/**
* Check whether JavaScript is enabled in the webview with universal resource access
* Holds if `ma` is a method invocation against `va` and `va.setJavaScriptEnabled(true)` occurs elsewhere in the program
*/
predicate isJSEnabled(MethodAccess ma) {
predicate isJSEnabled(Variable v) {
exists(VarAccess va, MethodAccess jsa |
ma.getQualifier() = va and
jsa.getQualifier() = va.getVariable().getAnAccess() and
v.getAnAccess() = va and
jsa.getQualifier() = v.getAnAccess() and
jsa.getMethod() instanceof AllowJavaScriptMethod and
jsa.getArgument(0).(BooleanLiteral).getBooleanValue() = true
)
}
/**
* Load URL method call on the `android.webkit.WebView` object
* Fetch URL method call on the `android.webkit.WebView` object
*/
class LoadResourceMethodAccess extends MethodAccess {
LoadResourceMethodAccess() {
class FetchResourceMethodAccess extends MethodAccess {
FetchResourceMethodAccess() {
this.getMethod().getDeclaringType() instanceof TypeWebView and
(
this.getMethod().hasName("loadUrl") or
@@ -75,12 +75,11 @@ class IntentGetExtraMethodAccess extends MethodAccess {
}
/**
* Source of loading urls
* Source of fetching urls
*/
class UntrustedResourceSource extends RemoteFlowSource {
UntrustedResourceSource() {
exists(MethodAccess ma |
ma instanceof IntentGetExtraMethodAccess and
exists(IntentGetExtraMethodAccess ma |
this.asExpr().(VarAccess).getVariable().getAnAssignedValue() = ma
)
}
@@ -88,52 +87,49 @@ class UntrustedResourceSource extends RemoteFlowSource {
override string getSourceType() {
result = "user input vulnerable to XSS and sensitive resource disclosure attacks" and
exists(MethodAccess ma |
ma.getMethod() instanceof AllowUniversalAccessMethod and //High precision match of unsafe resource loading
ma.getMethod() instanceof CrossOriginAccessMethod and //High precision match of unsafe resource fetching
ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true
)
or
result = "user input potentially vulnerable to XSS and sensitive resource disclosure attacks" and
not exists(MethodAccess ma |
ma.getMethod() instanceof AllowUniversalAccessMethod and //High precision match of unsafe resource loading
ma.getMethod() instanceof CrossOriginAccessMethod and //High precision match of unsafe resource fetching
ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true
)
}
}
/**
* load externally controlled data from loadUntrustedResource
* Holds if `ma` loads url `sink`
*/
predicate loadUntrustedResource(MethodAccess ma, Expr sink) {
ma instanceof LoadResourceMethodAccess and
sink = ma.getArgument(0)
}
predicate fetchResource(FetchResourceMethodAccess ma, Expr sink) { sink = ma.getArgument(0) }
/**
* Sink of loading urls
* Sink of fetching urls
*/
class UntrustedResourceSink extends DataFlow::ExprNode {
UntrustedResourceSink() { loadUntrustedResource(_, this.getExpr()) }
class UrlResourceSink extends DataFlow::ExprNode {
UrlResourceSink() { fetchResource(_, this.getExpr()) }
MethodAccess getMethodAccess() { loadUntrustedResource(result, this.getExpr()) }
FetchResourceMethodAccess getMethodAccess() { fetchResource(result, this.getExpr()) }
}
class LoadUntrustedResourceConfiguration extends TaintTracking::Configuration {
LoadUntrustedResourceConfiguration() { this = "LoadUntrustedResourceConfiguration" }
class FetchUntrustedResourceConfiguration extends TaintTracking::Configuration {
FetchUntrustedResourceConfiguration() { this = "FetchUntrustedResourceConfiguration" }
override predicate isSource(DataFlow::Node source) { source instanceof UntrustedResourceSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof UntrustedResourceSink }
override predicate isSink(DataFlow::Node sink) { sink instanceof UrlResourceSink }
}
from DataFlow::PathNode source, DataFlow::PathNode sink, LoadUntrustedResourceConfiguration conf
from DataFlow::PathNode source, DataFlow::PathNode sink, FetchUntrustedResourceConfiguration conf
where
exists(VarAccess webviewVa, MethodAccess getSettingsMa, MethodAccess ma |
exists(VarAccess webviewVa, MethodAccess getSettingsMa, Variable v |
conf.hasFlowPath(source, sink) and
sink.getNode().(UntrustedResourceSink).getMethodAccess().getQualifier() = webviewVa and
sink.getNode().(UrlResourceSink).getMethodAccess().getQualifier() = webviewVa and
webviewVa.getVariable().getAnAccess() = getSettingsMa.getQualifier() and
ma.getQualifier().(VarAccess).getVariable().getAnAssignedValue() = getSettingsMa and
isJSEnabled(ma)
v.getAnAssignedValue() = getSettingsMa and
isJSEnabled(v)
)
select sink.getNode().(UntrustedResourceSink).getMethodAccess(), source, sink,
"Unsafe resource loading in Android webview due to $@.", source.getNode(),
select sink.getNode().(UrlResourceSink).getMethodAccess(), source, sink,
"Unsafe resource fetching in Android webview due to $@.", source.getNode(),
source.getNode().(UntrustedResourceSource).getSourceType()

View File

@@ -1 +1,2 @@
| UnsafeAndroidAccess.java:29:3:29:21 | loadUrl(...) | UnsafeAndroidAccess.java:29:14:29:20 | thisUrl | UnsafeAndroidAccess.java:29:14:29:20 | thisUrl | Unsafe resource loading in Android webview due to $@. | UnsafeAndroidAccess.java:29:14:29:20 | thisUrl | user input vulnerable to XSS and sensitive resource disclosure attacks |
| UnsafeAndroidAccess.java:30:3:30:21 | loadUrl(...) | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | user input vulnerable to XSS and sensitive resource disclosure attacks |
| UnsafeAndroidAccess.java:53:3:53:21 | loadUrl(...) | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | user input vulnerable to XSS and sensitive resource disclosure attacks |

View File

@@ -7,7 +7,8 @@ import android.webkit.WebView;
import android.webkit.WebViewClient;
public class UnsafeAndroidAccess extends Activity {
public void onCreate(Bundle savedInstanceState) {
//Test onCreate with both JavaScript and universal resource access enabled while taking remote user inputs from bundle extras
public void testOnCreate1(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
@@ -28,4 +29,69 @@ public class UnsafeAndroidAccess extends Activity {
String thisUrl = getIntent().getExtras().getString("url");
wv.loadUrl(thisUrl);
}
//Test onCreate with both JavaScript and universal resource access enabled while taking remote user inputs from string extra
public void testOnCreate2(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
WebView wv = (WebView) findViewById(-1);
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccessFromFileURLs(true);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
String thisUrl = getIntent().getStringExtra("url");
wv.loadUrl(thisUrl);
}
//Test onCreate with both JavaScript and universal resource access disabled by default while taking remote user inputs
public void testOnCreate3(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
WebView wv = (WebView) findViewById(-1);
WebSettings webSettings = wv.getSettings();
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
String thisUrl = getIntent().getStringExtra("url");
wv.loadUrl(thisUrl);
}
//Test onCreate with both JavaScript and universal resource access enabled while not taking remote user inputs
public void testOnCreate4(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
WebView wv = (WebView) findViewById(-1);
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccessFromFileURLs(true);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
wv.loadUrl("https://www.mycorp.com");
}
}

View File

@@ -1 +1 @@
experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql
experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql