点击蓝字 关注我们
(本文由小貂快跑代发,原作者为“IntSheep”)
00
前言
01
加载动态字节码
02
类加载器
03
利用TemplatesImpl加载字节码
3.1
明确利用链的调用
TemplatesImpl#getOutputProperties()
-> TemplatesImpl#newTransformer()
-> TemplatesImpl#getTransletInstance()
-> TemplatesImpl#defineTransletClasses()
-> TransletClassLoader#defineClass()
public static void main(String[] args) throws Exception {
byte[] code =
Base64.getDecoder().decode("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwADgAPBwAbDAAcAB0BABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwAeDAAfACABABJIZWxsb1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAIAAsAAAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAACgALAAAABAABAAwAAQAOAA8AAQAJAAAALQACAAEAAAANKrcAAbIAAhIDtgAEsQAAAAEACgAAAA4AAwAAAA0ABAAOAAwADwABABAAAAACABE=");
TemplatesImpl obj = new TemplatesImpl();
obj.newTransformer();
}
import base64
import io
with open("<class文件路径>","rb")as f1:
bin_date=f1.read()
Byte_data=io.BytesIO(bin_date)
base64_data=base64.b64encode(Byte_data.read()).decode()
print(base64_data)
3.2
明确参数的输入
defineClass()
首当其冲的就是defineClass(),它负责加载动态字节码,因此它的参数必须是动态字节码。
在defineTransletClasses()调用的defineClass()传入的参数是 _bytecodes,是一个默认值为null的变量。
因此我们使用反射设置_bytecodes的值为动态字节码。
setFieldValue(obj, "_bytecodes", new byte[][] {code});
当然以上设置参数不能直接使用setFieldValue,要将其封装成一个函数:
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Class<?> objClass = obj.getClass();
Field field = objClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
defineTransletClasses()
没有一定要传什么参数不然不能执行的。
getTransletInstance()
这里就要求_name不能为null,并且_class得为null。
newTransformer()
这个方法的调用不仅要确保getTransletInstance()会被执行,而且还得确保return这一步会被执行,毕竟这是利用链的开头,我们是要创建实例对象的,必须确保能获取实例对象。
好像没有什么参数必须设置的。
直接运行。
3.3
根据报错调整
第一次调整
运行之后报错了,发现报的是有参数为null的错误。
我们跟进第一个报错看一下。
发现_tfactory不能为null。
这个参数是个TransformerFactoryImpl类型的,直接使用反射给他一个初始值。
setFieldValue(obj,"_tfactory",new TransformerFactoryImpl());
第二次调整
再上面调整的基础上再运行一遍,发现和前面一样的错误。直接定位到第一个报错继续查看。
发现的报错其实是在defineClass方法下面的,不会影响defineClass方法的调用,我们不必理会!
第三次调整
上面运行没有问题的话,为什么字节码不会被加载呢?查阅了资料发现TemplatesImpl 中对加载的字节码是有一定要求的:这个字节码对应的类必须是com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet 的子类。
因此只需创建一个新的类继承AbstractTranslet,并将其转化为字节码就好了。
继承后可以跟进IntellJ提示添加父类的构造函数。并自己写一个构造方法,打印出“Hello Templatempl”。
完整代码如下
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Field;
import java.util.Base64;
public class HelloDefineClass {
public HelloDefineClass() {
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Class<?> objClass = obj.getClass();
Field field = objClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
byte[] code = Base64.getDecoder().decode("yv66vgAAADcALAoABgAdCQAeAB8IACAKACEAIgcAIwcAJAEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQATTEhlbGxvVGVtcGxhdGVzbXBsOwEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApFeGNlcHRpb25zBwAlAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAAY8aW5pdD4BAAMoKVYBAApTb3VyY2VGaWxlAQAWSGVsbG9UZW1wbGF0ZXNtcGwuamF2YQwAGQAaBwAmDAAnACgBABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwApDAAqACsBABFIZWxsb1RlbXBsYXRlc21wbAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABBqYXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAPwAAAAMAAAABsQAAAAIACgAAAAYAAQAAAAsACwAAACAAAwAAAAEADAANAAAAAAABAA4ADwABAAAAAQAQABEAAgASAAAABAABABMAAQAHABQAAgAJAAAASQAAAAQAAAABsQAAAAIACgAAAAYAAQAAABAACwAAACoABAAAAAEADAANAAAAAAABAA4ADwABAAAAAQAVABYAAgAAAAEAFwAYAAMAEgAAAAQAAQATAAEAGQAaAAEACQAAAD8AAgABAAAADSq3AAGyAAISA7YABLEAAAACAAoAAAAOAAMAAAASAAQAEwAMABQACwAAAAwAAQAAAA0ADAANAAAAAQAbAAAAAgAc");
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{code});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
obj.newTransformer();
}
}
运行结果如下:
成功运行!
总结
SUMMARY
TemplatesImpl这个类在Java代码审计里面有着举足轻重的地位,许多利用链都使用到了它。Java代码审计,特别是反序列方面的审计,调用的函数很多,需要正向和反向多次看一下利用链,才明白为什么这样调用、参数为什么这样设置。另外,大家可以尝试一下笔者所说的书签功能,在链子一长的情况下真的很好用。
// 作者:IntSheep
在校大学生,绿盟科技暑期优秀渗透测试实习生。熟悉Web安全、应急响应、Java代码审计以及内网渗透。2023演练在某单位进行主防,参与多次应急响应。
原文始发于微信公众号(赛博游民营):JAVA代码审计:深入分析Java中TemplatesImpl类加载动态字节码的过程
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论