前言

乱七八糟的东西。


readObject

找一找Java里有没有其他不受黑白名单限制,又可以通过反序列化触发的反序列化点。

简单来说就是存在重开InputStream操作的类,要么是可序列化类,要么存在发起连接操作,不然数据就要通过别的可序列化类传递过来,不好控制。

有一些叫做getObject的函数,比如SignedObject、SealedObject,然而没有什么会调用他们的地方。

MarshalledObject

其get函数如下:

public T get() throws IOException, ClassNotFoundException {
    if (objBytes == null)   // must have been a null object
        return null;

    ByteArrayInputStream bin = new ByteArrayInputStream(objBytes);
    // locBytes is null if no annotations
    ByteArrayInputStream lin =
        (locBytes == null ? null : new ByteArrayInputStream(locBytes));
    MarshalledObjectInputStream in =
        new MarshalledObjectInputStream(bin, lin, objectInputFilter);
    @SuppressWarnings("unchecked")
    T obj = (T) in.readObject();
    in.close();
    return obj;
}

看起来是可控的,然而看到这里有一个碍眼的objectInputFilter,它在readObject时会被赋值:

private void readObject(ObjectInputStream stream)
    throws IOException, ClassNotFoundException {
    stream.defaultReadObject();     // read in all fields
    objectInputFilter = ObjectInputFilter.Config.getObjectInputFilter(stream);
}

getObjectInputFilter会从原本的InputStream中取出其InputFilter:

public static ObjectInputFilter getObjectInputFilter(ObjectInputStream var0) {
    Objects.requireNonNull(var0, (String)"inputStream");
    return SharedSecrets.getJavaOISAccess().getObjectInputFilter(var0);
}

所以原本反序列化流程中存在的InputFilter也会影响这里的反序列化。

想要通过反序列化操作调用这里的get函数,就要调用ActivatableRef类的invoke函数,即RemoteRef接口的invoke函数,但是反序列化过程中方便触发的点位于RemoteObjectInvocationHandler函数中,需要使用动态代理,还需要代理的接口继承了Remote。

整条利用链应该跟以前学习过的JRMP绕过类似,这里就不验证了。

RegistryImpl_Skel

即RMI中注册中心使用的反序列化点,该类不可序列化。

当通过LocateRegistry.createRegistry创建注册中心时会实例化一个RegistryImpl对象:

public static Registry createRegistry(int port) throws RemoteException {
    return new RegistryImpl(port);
}

其构造函数会开始建立监听并通过registryFilter函数注册反序列化白名单:

public RegistryImpl(final int var1) throws RemoteException {
    this.bindings = new Hashtable(101);
    if (var1 == 1099 && System.getSecurityManager() != null) {
        try {
            AccessController.doPrivileged((PrivilegedExceptionAction)(new PrivilegedExceptionAction<Void>() {
                public Void run() throws RemoteException {
                    LiveRef var1x = new LiveRef(RegistryImpl.id, var1);
                    RegistryImpl.this.setup(new UnicastServerRef(var1x, (var0) -> {
                        return RegistryImpl.registryFilter(var0);
                    }));
                    return null;
                }
            }), (AccessControlContext)null, new SocketPermission("localhost:" + var1, "listen,accept"));
        } 
        ...
    } else {
        LiveRef var2 = new LiveRef(id, var1);
        this.setup(new UnicastServerRef(var2, RegistryImpl::registryFilter));
    }

}

RegistryImpl_Stub

位于其list函数和lookup函数中,依旧是RMI中注册中心使用的反序列化点,该类不可序列化。

同样受到白名单影响。

UnicastRef

即RMI中服务端使用的反序列化点,其unmarshalValue函数中会有一个反序列化操作,用于反序列化客户端传输过来的对象。

可反序列化,有两条路可以来到该反序列化点,第一是跟MarshalledObject类似,同样是调用RemoteRef接口的invoke函数,但是需要满足的条件更多,需要代理的函数返回类型不是Int、Boolean等主要类型。

第二就是走正常途径,通过反序列化搞定一个RMI服务端了,听起来就不可靠。

DGCImpl_Skel

位于dispatch函数中,即JRMPListener利用链使用的反序列化点,会受到DGCImpl类的白名单影响。

DGCImpl_Stub

位于dirty函数中,即JRMPClient利用链使用的反序列化点,同样会受到DGCImpl类的白名单影响。

StreamRemoteCall

位于executeCall函数中,同样有两条路径,第一条与MarshalledObject相似,通过RemoteRef接口的invoke函数触发,即以前学习过的JRMP反序列化绕过利用链。

第二条不怎么可信,走DGCImpl_Stub的clean、dirty函数会受到白名单影响,走RegistryImpl_Stub的话它的ref成员又无法通过反序列化控制。

触发RemoteRef接口的invoke函数

可控触发点位于RemoteObjectInvocationHandler类中,需要用其代理一个Remote子接口中定义的函数才能触发。

RMIServer

存在一个getVersion函数,或许可以跟fastjson之类的会调用getter的东西一起用?

ActivationInstantiator

往上追又到了Activator接口的activate函数,没必要看了。

Activator

有一个activate函数,要通过RemoteRef接口的invoke函数触发。


参考


Web Java 反序列化 JRMP JNDI

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

SpringBoot框架下的JNDI利用法探索
机器学习入门5