{"record":{"id":"37c4c76122a41915","repo":"frohoff/ysoserial","slug":"not-allowed-to-read-object","errorCode":null,"errorMessage":"Not allowed to read object","messagePattern":"Not allowed to read object","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"warning","filePath":"src/main/java/ysoserial/exploit/JRMPListener.java","lineNumber":256,"sourceCode":"        }\n\n        s.close();\n    }\n\n\n    private void doCall ( DataInputStream in, DataOutputStream out, Object payload ) throws Exception {\n        ObjectInputStream ois = new ObjectInputStream(in) {\n\n            @Override\n            protected Class<?> resolveClass ( ObjectStreamClass desc ) throws IOException, ClassNotFoundException {\n                if ( \"[Ljava.rmi.server.ObjID;\".equals(desc.getName())) {\n                    return ObjID[].class;\n                } else if (\"java.rmi.server.ObjID\".equals(desc.getName())) {\n                    return ObjID.class;\n                } else if ( \"java.rmi.server.UID\".equals(desc.getName())) {\n                    return UID.class;\n                }\n                throw new IOException(\"Not allowed to read object\");\n            }\n        };\n\n        ObjID read;\n        try {\n            read = ObjID.read(ois);\n        }\n        catch ( java.io.IOException e ) {\n            throw new MarshalException(\"unable to read objID\", e);\n        }\n\n\n        if ( read.hashCode() == 2 ) {\n            ois.readInt(); // method\n            ois.readLong(); // hash\n            System.err.println(\"Is DGC call for \" + Arrays.toString((ObjID[])ois.readObject()));\n        }\n","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/frohoff/ysoserial/blob/218bcffcaaa904a4e392f0c15d9e2874533635a3/src/main/java/ysoserial/exploit/JRMPListener.java#L238-L274","documentation":"JRMPListener installs a strict ObjectInputStream whose resolveClass only permits a small whitelist (ObjID[], ObjID, UID) so unmarshalling attacker-controlled data cannot instantiate arbitrary classes. Any other class name in the stream causes IOException(\"Not allowed to read object\"). This is a deliberate deserialization-gadget defense, not a bug.","triggerScenarios":"During doCall/doMessage, the ObjectInputStream encounters a class descriptor whose name is not exactly one of 'java.rmi.server.ObjID[]', 'java.rmi.server.ObjID', or 'java.rmi.server.UID' — e.g. a client sends a serialized object graph with extra classes.","commonSituations":"A real RMI client (or exploit payload) sends serialized objects beyond the minimal handshake the listener expects; a client's stream includes string or other object types the whitelist does not cover.","solutions":["Confirm the client only sends the minimal JRMP handshake objects (ObjID, UID, primitive fields)","If you control the payload, strip extra serialized objects from the stream","If a new required type is legitimate, extend the whitelist in resolveClass deliberately (understand the deserialization risk)","Do not whitelist arbitrary classes — that reintroduces the gadget vulnerability"],"exampleFix":"// before\n} else if ( \"java.rmi.server.UID\".equals(desc.getName())) {\n    return UID.class;\n}\nthrow new IOException(\"Not allowed to read object\");\n// after\n} else if ( \"java.rmi.server.UID\".equals(desc.getName())) {\n    return UID.class;\n} else if ( \"java.lang.String\".equals(desc.getName())) { // only if genuinely needed\n    return String.class;\n}\nthrow new IOException(\"Not allowed to read object\");","handlingStrategy":"validation","validationCode":"// client side: ensure the stream only contains whitelisted types\n// allowed: ObjID, UID, primitive fields; strip any other Serializable objects first","typeGuard":"static boolean isWhitelisted(Class<?> c) {\n    return c == ObjID.class || c == ObjID[].class || c == UID.class;\n}","tryCatchPattern":"try {\n    sendHandshake(stream);\n} catch (IOException e) {\n    if (e.getMessage().equals(\"Not allowed to read object\")) {\n        // remove non-whitelisted objects from the outgoing stream\n    }\n}","preventionTips":["Only send the minimal JRMP handshake objects the listener whitelists","Never extend the whitelist casually — it is a deserialization defense","Test the handshake against the listener before integrating into tooling"],"tags":["java","security","deserialization","rmi"],"backgroundTag":"invalid-argument-value","analyzedSha":"218bcffcaaa904a4e392f0c15d9e2874533635a3","analyzedAt":"2026-09-12T01:53:58.488Z","contentChangedAt":"2026-09-12T01:53:58.488Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}