/** * This is a copy of UnsafeDeserialization.qll from the QL standard library * for Java. It is copied here for the purpose of recreating the scenario * when Mo first discovered the vulnerability. At the time, the standard * unsafe deserialization query (UnsafeDeserialization.ql) did not detect the * bug. Mo discovered the vulnerability by customizing the standard query, * using knowledge that he had about the design of Struts. (The standard query * has since been improved so that it will automatically detect similar * vulnerabilities in future.) */ import semmle.code.java.frameworks.Kryo import semmle.code.java.frameworks.XStream import semmle.code.java.dataflow.DataFlow class ObjectInputStreamReadObjectMethod extends Method { ObjectInputStreamReadObjectMethod() { this.getDeclaringType().getASourceSupertype*().hasQualifiedName("java.io", "ObjectInputStream") and (this.hasName("readObject") or this.hasName("readUnshared")) } } class XMLDecoderReadObjectMethod extends Method { XMLDecoderReadObjectMethod() { this.getDeclaringType().hasQualifiedName("java.beans", "XMLDecoder") and this.hasName("readObject") } } class SafeXStream extends DataFlow::Configuration { SafeXStream() { this = "UnsafeDeserialization::SafeXStream" } override predicate isSource(DataFlow::Node src) { any(XStreamEnableWhiteListing ma).getQualifier().(VarAccess).getVariable().getAnAccess() = src.asExpr() } override predicate isSink(DataFlow::Node sink) { exists(MethodAccess ma | sink.asExpr() = ma.getQualifier() and ma.getMethod() instanceof XStreamReadObjectMethod ) } } class SafeKryo extends DataFlow::Configuration { SafeKryo() { this = "UnsafeDeserialization::SafeKryo" } override predicate isSource(DataFlow::Node src) { any(KryoEnableWhiteListing ma).getQualifier().(VarAccess).getVariable().getAnAccess() = src.asExpr() } override predicate isSink(DataFlow::Node sink) { exists(MethodAccess ma | sink.asExpr() = ma.getQualifier() and ma.getMethod() instanceof KryoReadObjectMethod ) } } predicate unsafeDeserialization(MethodAccess ma, Expr sink) { exists(Method m | m = ma.getMethod() | m instanceof ObjectInputStreamReadObjectMethod and sink = ma.getQualifier() or m instanceof XMLDecoderReadObjectMethod and sink = ma.getQualifier() or m instanceof XStreamReadObjectMethod and sink = ma.getAnArgument() and not exists(SafeXStream sxs | sxs.hasFlowToExpr(ma.getQualifier())) or m instanceof KryoReadObjectMethod and sink = ma.getAnArgument() and not exists(SafeKryo sk | sk.hasFlowToExpr(ma.getQualifier())) ) } class UnsafeDeserializationSink extends DataFlow::ExprNode { UnsafeDeserializationSink() { unsafeDeserialization(_, this.getExpr()) } MethodAccess getMethodAccess() { unsafeDeserialization(result, this.getExpr()) } }