Java由反射到命令执行

0x00 什么是Java反射和应用场景

  • 详细的反射教程可以参考下面的链接,讲得超级详细,简单讲就是在程序运行时动态获得类的属性和方法,并可以动态创建对象

关于反射的详解教程请点我

  • 应用场景

当我们在使用IDE(如Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法,这里就会用到反射。反射最重要的用途就是开发各种通用框架。
很多框架(比如Spring)都是配置化的(比如通过XML文件配置JavaBean,Action之类的),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。

  • 简单介绍几个主要方法,详解的用法可以参考我上面给的链接
  1. 加载类,得到指定类的Class对象
Class clazz = Class.forName("com.yano.reflect.Consumer");
Class clazz1 = new Consumer().getClass();
Class class2 = Person.class;
  1. 创建实例
方法一:
Class<?> c = String.class;
Object str = c.newInstance();
方法二:
//获取String所对应的Class对象
Class<?> c = String.class;
//获取String类带一个String参数的构造器
Constructor constructor = c.getConstructor(String.class);
//根据构造器创建实例
Object obj = constructor.newInstance("23333");
System.out.println(obj);
  1. 获取方法
getMethod方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象

public Method getMethod(String name, Class<?>... parameterTypes)

0x02 Java的命令执行类:

  • java.lang.Runtime类,下面是官方文档解释
public class Runtime extends Object

Every Java application has a single instance of class Runtime that allows the application to interface with the environment in which the application is running. The current runtime can be obtained from the getRuntime method.
An application cannot create its own instance of this class.
  • 其中主要的方法是:getRuntime(),得到一个和当前程序相关联的Runtime类的对象,文档解释如下:
public static Runtime getRuntime()
Returns the runtime object associated with the current Java application. Most of the methods of class Runtime are instance methods and must be invoked with respect to the current runtime object.
Returns:
the Runtime object associated with the current Java application.
  • 这个Runtime对象再调用exec()方法执行命令,详细文档解释如下:
public Process exec(String command)
throws IOException
Executes the specified string command in a separate process.
This is a convenience method. An invocation of the form exec(command) behaves in exactly the same way as the invocation exec(command, null, null).
  • 最后综合使用,在我的mac打开了计算器程序
public class Main {

public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, IOException {
Runtime.getRuntime().exec("open /Applications/Calculator.app/");
}
}

0x03 命令执行漏洞的实现,如何取得Runtime类是关键,其中一种常用方式就是通过反射

  1. 场景一 Android API17 前的Webview加了addJavascriptInterface()方法,映射了一个Java对象到js中
public class WebAppInterface {
Context mContext;

/** Instantiate the interface and set the context */
WebAppInterface(Context c) {
mContext = c;
}

/*Show a toast from the web page */
/*@JavascriptInterface /*新的要求,加注解,API17以上*/
public void showToast(String toast) {
Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
}
}


WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");
  • 上面这段Android代码中,把WebAppInterface类映射到了js中的的Android对象上,所以可以在js中调用WebAppInterface类的方法

对于强迫症患者,一定会疑惑这个“Android”在js中算什么,类?还是实例?,下面一段是Google文档的解释,Webview帮我们初始化好这个js的”Android”对象,可以直接在JavaScript中调用

You can bind this class to the JavaScript that runs in your WebView with addJavascriptInterface() and name the interface Android. For example:
This creates an interface called Android for JavaScript running in the WebView. At this point, your web application has access to the WebAppInterface class. For example, here’s some HTML and JavaScript that creates a toast message using the new interface when the user clicks a button:
There’s no need to initialize the Android interface from JavaScript. The WebView automatically makes it available to your web page. So, at the click of the button, the showAndroidToast() function uses the Android interface to call the WebAppInterface.showToast() method.

  • 结合前面的Java反射知识,既然暴露了Java对象给JavaScript操作,首先可以通过这个这个对象的“getClass()”方法获得对应类的Class对象,然后调用Class对象的”forname()”方法加载指定的“Runtime”类,这个特殊的“Runtime”类再调用”getmethod()”方法调用类里面的getruntime方法,返回一个Method对象,最后调用”invoke()”方法执行Runtime类的exec()方法。。。这就是整个思路了
package com.company.test;
import java.io.IOException;
import java.lang.reflect.*;

class WebAppInterface {

public void showToast(String toast) {
System.out.println("just a test class");
}
}

public class Main {

public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException,
InstantiationException, IOException, NoSuchMethodException, InvocationTargetException {
WebAppInterface Android = new WebAppInterface();/* 模拟一个可以被js调用的实例 */
Method method = Android.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null);
Runtime run = (Runtime) method.invoke(null,null);
run.exec("open /Applications/Calculator.app/");

}

}

效果图

0x04 Runtime类的限制

不同环境下命令的编写有差异,例如Bash,powershell,python,perl,所以我这里找来了一个在线工具自动格式化相应环境的命令

http://jackson.thuraisamy.me/runtime-exec-payloads.html

Bonus

关于Java泛型的一点讲解:泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

详细的Java 泛型讲解请点击我

  1. 不用泛型直接通过反射创建类的实例,需要进行强制类型转换
Consumer clazz;
clazz = (Consumer) Class.forName("com.company.hsbc.Consumer").newInstance();
  1. 用object类型可以解决问题
Object clazz;
clazz = Class.forName("com.company.hsbc.Consumer").newInstance();

参考链接: