Merge pull request #6456 from smowton/smowton/admin/flexjson-unsafe-deserialization

Java: add unsafe-deserialization support for Flexjson
This commit is contained in:
Anders Schack-Mulligen
2021-09-14 14:33:22 +02:00
committed by GitHub
11 changed files with 423 additions and 2 deletions

View File

@@ -81,6 +81,7 @@ private module Frameworks {
private import semmle.code.java.frameworks.ApacheHttp
private import semmle.code.java.frameworks.apache.Collections
private import semmle.code.java.frameworks.apache.Lang
private import semmle.code.java.frameworks.Flexjson
private import semmle.code.java.frameworks.guava.Guava
private import semmle.code.java.frameworks.jackson.JacksonSerializability
private import semmle.code.java.frameworks.JavaxJson

View File

@@ -0,0 +1,41 @@
/**
* Provides classes for working with the Flexjson framework.
*/
import java
private import semmle.code.java.dataflow.ExternalFlow
/** The class `flexjson.JSONDeserializer`. */
class FlexjsonDeserializer extends RefType {
FlexjsonDeserializer() { this.hasQualifiedName("flexjson", "JSONDeserializer") }
}
/** The class `flexjson.ObjectFactory`. */
class FlexjsonObjectFactory extends RefType {
FlexjsonObjectFactory() { this.hasQualifiedName("flexjson", "ObjectFactory") }
}
/** The deserialization method `deserialize`. */
class FlexjsonDeserializeMethod extends Method {
FlexjsonDeserializeMethod() {
this.getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof
FlexjsonDeserializer and
this.getName() = "deserialize" and
not this.getAParameter().getType() instanceof FlexjsonObjectFactory // deserialization method with specified class types in object factory is unlikely to be vulnerable
}
}
/** The method `use` to configure allowed class type. */
class FlexjsonDeserializerUseMethod extends Method {
FlexjsonDeserializerUseMethod() {
this.getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof
FlexjsonDeserializer and
this.hasName("use")
}
}
private class FluentUseMethodModel extends SummaryModelCsv {
override predicate row(string r) {
r = "flexjson;JSONDeserializer;true;use;;;Argument[-1];ReturnValue;value"
}
}

View File

@@ -16,6 +16,7 @@ private import semmle.code.java.frameworks.Castor
private import semmle.code.java.frameworks.Jackson
private import semmle.code.java.frameworks.Jabsorb
private import semmle.code.java.frameworks.JoddJson
private import semmle.code.java.frameworks.Flexjson
private import semmle.code.java.frameworks.apache.Lang
private import semmle.code.java.Reflection
@@ -203,6 +204,9 @@ predicate unsafeDeserialization(MethodAccess ma, Expr sink) {
// jodd.json.JsonParser may be configured for unrestricted deserialization to user-specified types
joddJsonParserConfiguredUnsafely(ma.getQualifier())
)
or
m instanceof FlexjsonDeserializeMethod and
sink = ma.getArgument(0)
)
}
@@ -270,9 +274,48 @@ class UnsafeDeserializationConfig extends TaintTracking::Configuration {
not ma.getArgument(1).getType().getName() = ["Class<Object>", "Class<?>"] and
node.asExpr() = ma.getAnArgument()
)
or
exists(MethodAccess ma |
// Sanitize the input to flexjson.JSONDeserializer.deserialize whenever it appears
// to be called with an explicit class argument limiting those types that can
// be instantiated during deserialization, or if the deserializer has already been
// configured to use a specified root class.
ma.getMethod() instanceof FlexjsonDeserializeMethod and
node.asExpr() = ma.getAnArgument() and
(
ma.getArgument(1).getType() instanceof TypeClass and
not ma.getArgument(1) instanceof NullLiteral and
not ma.getArgument(1).getType().getName() = ["Class<Object>", "Class<?>"]
or
isSafeFlexjsonDeserializer(ma.getQualifier())
)
)
}
}
/**
* Gets a safe usage of the `use` method of Flexjson, which could be:
* use(String, ...) where the path is null or
* use(ObjectFactory, String...) where the string varargs (or array) contains null
*/
MethodAccess getASafeFlexjsonUseCall() {
result.getMethod() instanceof FlexjsonDeserializerUseMethod and
(
result.getMethod().getParameterType(0) instanceof TypeString and
result.getArgument(0) instanceof NullLiteral
or
result.getMethod().getParameterType(0) instanceof FlexjsonObjectFactory and
exists(NullLiteral e | e = result.getAnArgument())
)
}
/**
* Holds if `e` is a safely configured Flexjson `JSONDeserializer`.
*/
predicate isSafeFlexjsonDeserializer(Expr e) {
DataFlow::localExprFlow(getASafeFlexjsonUseCall().getQualifier(), e)
}
/** Holds if `fromNode` to `toNode` is a dataflow step that resolves a class. */
predicate resolveClassStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
exists(ReflectiveClassIdentifierMethodAccess ma |