Oracle WebLogic 最新补丁的绕过漏洞分析(CVE-2020-2883) - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

Oracle WebLogic 最新补丁的绕过漏洞分析(CVE-2020-2883)

h1apwn 资讯 2020-05-14 10:10:00
1174507
收藏

导语:今年早些时候,作者在博客中公开了一个Oracle WebLogic Server中的反序列化漏洞。

今年早些时候,作者在博客中公开了一个Oracle WebLogic Server中的反序列化漏洞。此漏洞是由Oracle补丁的,并分配了CVE-2020-2555。但是,VNPT ISC的研究员Quynh Le向ZDI提交了一个漏洞,该漏洞表明了可以绕过补丁利用此漏洞。Oracle 已通报此标记为CVE-2020-2883的漏洞可用于攻击。在此博客文章中,我们将详细介绍此最新补丁中的漏洞。

 https://www.zerodayinitiative.com/blog/2020/3/5/cve-2020-2555-rce-through-a-deserialization-bug-in-oracles-weblogic-server
 https://www.us-cert.gov/ncas/current-activity/2020/05/01/unpatched-oracle-weblogic-servers-vulnerable-cve-2020-2883
 https://www.zerodayinitiative.com/advisories/ZDI-20-570/

0x01  补丁绕过

CVE-2020-2555的原始补丁程序未修补以下gdaget利用链的部分:

 BadAttributeValueExpException.readObject()
   com.tangosol.util.filter.LimitFilter.toString() // <--- CVE-2020-2555在此处补丁
     com.tangosol.util.extractor.ChainedExtractor.extract()
         com.tangosol.util.extractor.ReflectionExtractor().extract()
             Method.invoke()
             //...
             com.tangosol.util.extractor.ReflectionExtractor().extract()
             Method.invoke()
                 Runtime.exec()

调用ChainedExtractor.extract()仍将导致远程执行代码,Quynh Le的报告显示,ChainedExtractor.extract()仍然可以通过ExtractorComparator和AbstractExtractor类进行访问。我们从查看compare()方法ExtractorComparator:开始分析

 public int compare(T o1, T o2) {
     Comparable a1 = (o1 instanceof InvocableMap.Entry) ? (Comparable)((InvocableMap.Entry)o1).extract(this.m_extractor) 
                                                                                 : (Comparable)this.m_extractor.extract(o1);
           Comparable a2 = (o2 instanceof InvocableMap.Entry) ? (Comparable)((InvocableMap.Entry)o2).extract(this.m_extractor)
                                                                                  : (Comparable)this.m_extractor.extract(o2);
          if (a1 == null)
     {
       return (a2 == null) ? 0 : -1;
     }
          if (a2 == null)
     {
       return 1;
     }
          return a1.compareTo(a2);
   }

如上所示,仍然可以通过设置ChainedExtractor.extract()为this.m_extractor的实例来调用ChainedExtractor。

同样,compare()也可以使用AbstractExtractor抽象类的方法。

 public  int compare(Object o1,Object o2){ return  SafeComparator 。compareSafe(null,extract(o1),extract(o2)); }

MultiExtractor类扩展AbstractExtractor可以到达ChainedExtractor.extract():

 public abstract class AbstractCompositeExtractor   extends AbstractExtractor   [...Truncated...]
   public class MultiExtractor
   extends AbstractCompositeExtractor
   [...Truncated...]
      public Object extract(Object oTarget) {
     if (oTarget == null)
     {
       return null;
     }
          ValueExtractor[] aExtractor = getExtractors();
     int cExtractors = aExtractor.length;
     Object[] aValue = new Object[cExtractors];
          for (int i = 0; i < cExtractors; i++)
     {
       aValue[i] = aExtractor[i].extract(oTarget);<-----------------------
     }
          return new ImmutableArrayList(aValue);
   }

0x02  完整利用链

为了开发一个完整的gadget利用链,我们需要有调用compare()的能力,从方法Comparator到达readObject()。使用的公开记录的方法有PriorityQueue,有如下gadget类:BeanShell1,Jython1,CommonsCollections2,CommonsBeanutils1,CommonsCollections4和Groovy1:

 java.util 。PriorityQueue 。readObject()
   java.util 。PriorityQueue 。heapify()
   java.util 。PriorityQueue 。siftDown()
   java.util 。PriorityQueue 。siftDownUsingComparator()

SiftUpUsingComparator()可以调用compare()任意方法Comparator:

 private void siftUpUsingComparator(int paramInt, E paramE) {
     while (paramInt > 0) {
       int i = paramInt - 1 >>> 1;
       Object object = this.queue[i];
       if (this.comparator.compare(paramE, object) >= 0)<----------------
         break; 
       this.queue[paramInt] = object;
       paramInt = i;
     } 
     this.queue[paramInt] = paramE;
   }

还有其他实现此目的的方法。例如,使用以下方法:

 javax.management 。BadAttributeValueExpException 。readObject()  
   com.tangosol.internal.sleepycat.persist.evolve 。变异。toString()
     java.util.concurrent 。ConcurrentSkipListMap $ SubMap 。尺寸()
     java.util.concurrent 。ConcurrentSkipListMap $ SubMap 。isBeforeEnd()
       java.util.concurrent 。ConcurrentSkipListMap 。cpr()

总而言之,toString()Mutations类的方法可能导致调用ConcurrentSkipListMap.size():

 ConcurrentSkipListMap$SubMap.class
     public int size() {
         Comparator cmp = m.comparator;
         long count = 0;
         for (ConcurrentSkipListMap.Node n = loNode(cmp);
             isBeforeEnd(n, cmp); = Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)count;
         }
   [...Truncated...]
        boolean isBeforeEnd(ConcurrentSkipListMap.Node n, Comparator cmp) {
         ....
         int c = cpr(cmp, k, hi); 0 || (c == 0 && !hiInclusive))
                 return false;
             return true;
         } 
   [...Truncated...]
      static final int cpr(Comparator c, Object x, Object y) {
         return (c != null) ? c.compare(x, y) : ((Comparable)x).compareTo(y); <--------
     }

从ConcurrentSkipListMap.size()中可以调用compare()任意方法Comparator。

0x03  演示漏洞利用

通过使用上述方法,针对ExtractorComparator编译了以下完整的gadget链:

 javax.management.BadAttributeValueExpException.readObject()  
   com.tangosol.internal.sleepycat.persist.evolve.Mutations.toString()
     java.util.concurrent.ConcurrentSkipListMap$SubMap.size()
     java.util.concurrent.ConcurrentSkipListMap$SubMap.isBeforeEnd()
       java.util.concurrent.ConcurrentSkipListMap.cpr()
         com.tangosol.util.comparator.ExtractorComparator.compare()

以下视频演示了此gadget链用于通过T3协议获得RCE。

 https://www.youtube.com/watch?v=HM3Z-I998b4

对于该AbstractExtractor示例,使用了以下链:

 java.util.PriorityQueue.readObject()
   java.util.PriorityQueue.heapify()
   java.util.PriorityQueue.siftDown()
   java.util.PriorityQueue.siftDownUsingComparator()
   com.tangosol.util.extractor.AbstractExtractor.compare()
     com.tangosol.util.extractor.MultiExtractor.extract()
       com.tangosol.util.extractor.ChainedExtractor.extract()
         //...
         Method.invoke()
             //...
           Runtime.exec()

以下视频演示了此gadget链用于通过T3协议获得RCE:

 https://youtu.be/juIucTRZUL8

0x04  通过HTTP利用漏洞

应当注意,此漏洞位于Coherence 库中。 在其代码路径中具有反序列化路径的任何具有Coherence 库的应用程序也容易受到攻击。一个产品示例是Oracle Business Intelligence,它部署在Oracle WebLogic上。

可以将这些gadget链与CVE-2020-2950 / ZDI-20-505结合使用,该工具由GreenDog的研究人员报告给ZDI,以通过HTTP实现远程代码执行。

此漏洞位于BIRemotingServlet中,会侦听TCP端口7780,并且不需要任何身份验证:

      BIRemotingServlet     oracle.bi.nanserver.fwk.servlet.as.BIRemotingServlet     1        BIRemotingServelet     /messagebroker/as/*        BIRemotingServlet     /messagebroker/cs/*

BIRemotingServlet 使用AMF(操作消息格式)与客户端进行通信。

 protected void handleRequest(HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse) 
  throws ServletException, IOException {
  
   [...Truncated...]
              RemotingSvs remotingSvs = BISvsManagerBase.getRemotingSvs(); <----------------------------------------------------
       remotingSvs.processCall(); <--------------------------------------------------------------------------------------------
       setContentType(paramHttpServletResponse, OutputForm.AMF3);
       paramHttpServletResponse.setContentLength(byteArrayOutputStream.size());
       byteArrayOutputStream.writeTo(paramHttpServletResponse.getOutputStream());
       paramHttpServletResponse.flushBuffer();
     }
   public int processCall() throws BISvsException {
    [...Truncated...]
        AMF3Packet aMF3Packet1 = deserializePacket(dataInputStream); <------------------------------------------------------------
       if (logger.isLoggable(Level.FINE)) {
         logger.fine("De-serialized request packet: " + aMF3Packet1.toString());
       }
   [...Truncated...]
   }
   oracle.bi.nanserver.fwk.util.remoting.RemotingSvsImpl.class
    public AMF3Packet deserializePacket(DataInputStream paramDataInputStream) throws BISvsException {
     try {
       AMFObjectInput aMFObjectInput = getAMF3DeSerializer(paramDataInputStream); <------------------------------------------------
       LegacyObjectInput legacyObjectInput = new LegacyObjectInput(paramDataInputStream, aMFObjectInput);
       AMF3Packet aMF3Packet = new AMF3Packet();
       aMF3Packet.deserialize(legacyObjectInput); <----------------------------------------------------------------------------------
       return aMF3Packet;
     }
     catch (Exception exception) {
       handleException(exception);
              return null;
     } 
   }
   public AMFObjectInput getAMF3DeSerializer(DataInputStream paramDataInputStream) throws BISvsException {
     try {
       Class clazz = (Class)amf3DeSerializerClass.get();
       if (clazz == null) {
         String str = (String)BISvsManagerBase.getContextSvs().getValue("amf3DeSerializer");
                  if (str == null || str.trim().length() == 0) {
           clazz = oracle.bi.nanserver.fwk.util.amf.AMF3ObjectInput.class; <--------------------------------------------------------
           amf3DeSerializerClass.compareAndSet(null, clazz);
           logger.info("Using default AMF3 De-Serializer");
   [...Truncated...]
   }

如图所示,当对AMF数据包进行反序列化时,可以通过调用AMF3ObjectInput来重建任意对象readComplexObject()。

 protected Object readComplexObject(GenericTypeInfo paramGenericTypeInfo) 
   throws ClassNotFoundException, IOException {
     try {
       int i = readAMF3IntegerVal();
       if ((i & true) == 0) {
         return getVisitedObject(i >> 1);
       }
       ClassMetadata classMetadata = readClassMetadata(i);
               String str = this.proxySvs.getConcreteClassName(classMetadata.name);
       if (str == null) {
         str = classMetadata.name;
       }
        //  CVE-2020-2950 patch
       //if (isBlacklisted(str))
       //{
       //  throw new SecurityException("Unsupport class type:" + str);
       //}
              Class clazz = Class.forName(str);
       ClassProxy classProxy = this.proxySvs.getProxy(clazz);
               Object object1 = classProxy.newInstance(clazz);
               int j = this.objectRefList.size();
       markObjectVisited(object1);
               if (classMetadata.externalizable) {
                  if (paramGenericTypeInfo != null) {
           classProxy.readExternal(new GenericResult(object1, paramGenericTypeInfo), this);
         } else {
           classProxy.readExternal(object1, this);
         }
              }
       else if (clazz == oracle.bi.nanserver.fwk.util.remoting.messages.RemotingMessage.class) {
         populateRemotingMessage(object1, classMetadata, classProxy);
       } else {
                  String[] arrayOfString = classMetadata.getFieldNames();
         Object[] arrayOfObject = new Object[arrayOfString.length];
         for (byte b = 0; b < arrayOfString.length; b++) {
           arrayOfObject[b] = readObject();
         }
         this.proxySvs.setFieldValues(object1, arrayOfString, arrayOfObject, classProxy);
                  if (classMetadata.dynamic) {
           while (true) {
             String str1 = readAMF3String();
             if (str1 == null || str1.length() == 0) {
               break;
             }
             Object object = readObject();
             this.proxySvs.setFieldValue(object1, str1, object, classProxy);
           } 
         }
       }

在此示例中,UnicastRef重构了一个对象,从而导致对远程对象的服务器端分布式垃圾回收器的调用,从而使我们能够响应任意的序列化对象。如上所述响应gadget链之一会产生RCE。

有关在Java AMF实现中利用Java反序列化的更多详细信息,请参阅Code White的这篇文章。gadget链已添加到ysoserial中,并且使用JRMP侦听器来利用此漏洞。

 https://codewhitesec.blogspot.com/2017/04/amf.html
 https://github.com/frohoff/ysoserial

以下视频演示了这一操作:

 https://www.youtube.com/embed/h1c0sfVZNO8

0x05  分析总结

有关Java反序列化漏洞的更多详细信息,请参阅Moritz Bechler的白皮书。Oracle的博客没有说明攻击的广泛性,但是其指导很明确:需要立即打补丁。他们还提供有关如何限制Oracle WebLogic Server的T3 / T3S协议流量的指南。Oracle补丁程序的下一个版本计划于2020年7月14日发布,我们将持续关注在该更新之后还剩下多少反序列化漏洞。

 https://github.com/mbechler/marshalsec/blob/master/marshalsec.pdf
 https://blogs.oracle.com/security/apply-april-2020-cpu
本文翻译自:https://www.zerodayinitiative.com/blog/2020/5/8/details-on-the-oracle-weblogic-vulnerability-being-exploited-in-the-wild如若转载,请注明原文地址:
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2022 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务