{"record":{"id":"7cb1dee3df2af1a7","repo":"frohoff/ysoserial","slug":"unable-to-read-objid","errorCode":null,"errorMessage":"unable to read objID","messagePattern":"unable to read objID","errorType":"exception","errorClass":"MarshalException","httpStatus":null,"severity":"error","filePath":"src/main/java/ysoserial/exploit/JRMPListener.java","lineNumber":265,"sourceCode":"            @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\n        System.err.println(\"Sending return with payload for obj \" + read);\n\n        out.writeByte(TransportConstants.Return);// transport op\n        ObjectOutputStream oos = new JRMPClient.MarshalOutputStream(out, this.classpathUrl);\n\n        oos.writeByte(TransportConstants.ExceptionalReturn);\n        new UID().write(oos);\n\n        BadAttributeValueExpException ex = new BadAttributeValueExpException(null);","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/frohoff/ysoserial/blob/218bcffcaaa904a4e392f0c15d9e2874533635a3/src/main/java/ysoserial/exploit/JRMPListener.java#L247-L283","documentation":"In JRMPListener.doCall, the listener reads the ObjID from the ObjectInputStream; if reading throws an IOException it rethrows as java.rmi.MarshalException(\"unable to read objID\", e). This means the JRMP call stream was truncated or malformed at the very first field of the call message.","triggerScenarios":"The incoming stream does not contain a well-formed ObjID at the point doCall expects it — stream ends early, bytes are corrupt, or the sender is not speaking the JRMP call protocol at all.","commonSituations":"Non-JRMP clients (probes, HTTP requests) hitting the listener port; a truncated packet due to network issues; a client with an incompatible protocol version sending a different framing.","solutions":["Verify the connecting client actually speaks Java RMI/JRMP","Inspect the wrapped cause (e.getCause()) to distinguish EOF from corrupt data","Ensure the client sends the complete call message (ObjID + method + hash)","Retry if the network may have truncated the stream"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// confirm the peer speaks JRMP before treating it as a call stream\n// e.g. verify the transport op byte matches TransportConstants.Call","typeGuard":null,"tryCatchPattern":"try {\n    listenerSession();\n} catch (MarshalException e) {\n    if (e.getMessage().equals(\"unable to read objID\")) {\n        // log cause; likely non-JRMP client or truncated stream\n    }\n}","preventionTips":["Keep scanners/probes off the JRMP listener port","Ensure clients send the complete call message (ObjID, method, hash)","Inspect the MarshalException cause for EOF vs corruption"],"tags":["java","rmi","jrmp","deserialization"],"backgroundTag":"protobuf-unmarshal-failed","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"}