Merge branch 'main' into fixHiddenTypesTestGenerator

This commit is contained in:
Benjamin Muskalla
2021-09-28 17:42:39 +02:00
308 changed files with 10928 additions and 1350 deletions

View File

@@ -4,12 +4,12 @@
<qhelp>
<overview>
<p>XSLT (Extensible Stylesheet Language Transformations) is a language for transforming XML
documents into other XML documents or other formats. Processing of unvalidated XSLT stylesheet can
let attacker to read arbitrary files from the filesystem or to execute arbitrary code.</p>
documents into other XML documents or other formats. Processing unvalidated XSLT stylesheets can
allow attackers to read arbitrary files from the filesystem or to execute arbitrary code.</p>
</overview>
<recommendation>
<p>The general recommendation is to not process untrusted XSLT stylesheets. If user provided
<p>The general recommendation is to not process untrusted XSLT stylesheets. If user-provided
stylesheets must be processed, enable the secure processing mode.</p>
</recommendation>
@@ -17,7 +17,7 @@ stylesheets must be processed, enable the secure processing mode.</p>
<p>In the following examples, the code accepts an XSLT stylesheet from the user and processes it.
</p>
<p>In the first example, the user provided XSLT stylesheet is parsed and processed.</p>
<p>In the first example, the user-provided XSLT stylesheet is parsed and processed.</p>
<p>In the second example, secure processing mode is enabled.</p>

View File

@@ -1,6 +1,6 @@
/**
* @name XSLT transformation with user-controlled stylesheet
* @description Doing an XSLT transformation with user-controlled stylesheet can lead to
* @description Performing an XSLT transformation with user-controlled stylesheets can lead to
* information disclosure or execution of arbitrary code.
* @kind path-problem
* @problem.severity error
@@ -11,8 +11,7 @@
*/
import java
import semmle.code.java.dataflow.FlowSources
import XsltInjectionLib
import semmle.code.java.security.XsltInjectionQuery
import DataFlow::PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink, XsltInjectionFlowConfig conf

View File

@@ -4,7 +4,7 @@
<overview>
<p>
The Spring Expression Language (SpEL) is a powerful expression language
provided by Spring Framework. The language offers many features
provided by the Spring Framework. The language offers many features
including invocation of methods available in the JVM.
If a SpEL expression is built using attacker-controlled data,
and then evaluated in a powerful context,
@@ -31,7 +31,7 @@ that doesn't allow arbitrary method invocation.
<example>
<p>
The following example uses untrusted data to build a SpEL expression
and then runs it in the default powerfull context.
and then runs it in the default powerful context.
</p>
<sample src="UnsafeSpelExpressionEvaluation.java" />
@@ -53,4 +53,4 @@ However, it's recommended to avoid using untrusted input in SpEL expressions.
<a href="https://owasp.org/www-community/vulnerabilities/Expression_Language_Injection">Expression Language Injection</a>.
</li>
</references>
</qhelp>
</qhelp>

View File

@@ -11,9 +11,10 @@
*/
import java
import SpelInjectionLib
import semmle.code.java.security.SpelInjectionQuery
import semmle.code.java.dataflow.DataFlow
import DataFlow::PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink, ExpressionInjectionConfig conf
from DataFlow::PathNode source, DataFlow::PathNode sink, SpelInjectionConfig conf
where conf.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "SpEL injection from $@.", source.getNode(), "this user input"

View File

@@ -1,288 +0,0 @@
import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.security.XmlParsers
import DataFlow
/**
* A taint-tracking configuration for unvalidated user input that is used in XSLT transformation.
*/
class XsltInjectionFlowConfig extends TaintTracking::Configuration {
XsltInjectionFlowConfig() { this = "XsltInjectionFlowConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof XsltInjectionSink }
override predicate isSanitizer(DataFlow::Node node) {
node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
xmlStreamReaderStep(node1, node2) or
xmlEventReaderStep(node1, node2) or
staxSourceStep(node1, node2) or
documentBuilderStep(node1, node2) or
domSourceStep(node1, node2) or
newTransformerOrTemplatesStep(node1, node2) or
newTransformerFromTemplatesStep(node1, node2) or
xsltCompilerStep(node1, node2) or
xsltExecutableStep(node1, node2) or
xsltPackageStep(node1, node2)
}
}
/** The class `javax.xml.transform.stax.StAXSource`. */
class TypeStAXSource extends Class {
TypeStAXSource() { this.hasQualifiedName("javax.xml.transform.stax", "StAXSource") }
}
/** The class `javax.xml.transform.dom.DOMSource`. */
class TypeDOMSource extends Class {
TypeDOMSource() { this.hasQualifiedName("javax.xml.transform.dom", "DOMSource") }
}
/** The interface `javax.xml.transform.Templates`. */
class TypeTemplates extends Interface {
TypeTemplates() { this.hasQualifiedName("javax.xml.transform", "Templates") }
}
/** The method `net.sf.saxon.s9api.XsltTransformer.transform`. */
class XsltTransformerTransformMethod extends Method {
XsltTransformerTransformMethod() {
this.getDeclaringType().hasQualifiedName("net.sf.saxon.s9api", "XsltTransformer") and
this.hasName("transform")
}
}
/** The method `net.sf.saxon.s9api.Xslt30Transformer.transform`. */
class Xslt30TransformerTransformMethod extends Method {
Xslt30TransformerTransformMethod() {
this.getDeclaringType().hasQualifiedName("net.sf.saxon.s9api", "Xslt30Transformer") and
this.hasName("transform")
}
}
/** The method `net.sf.saxon.s9api.Xslt30Transformer.applyTemplates`. */
class Xslt30TransformerApplyTemplatesMethod extends Method {
Xslt30TransformerApplyTemplatesMethod() {
this.getDeclaringType().hasQualifiedName("net.sf.saxon.s9api", "Xslt30Transformer") and
this.hasName("applyTemplates")
}
}
/** The method `net.sf.saxon.s9api.Xslt30Transformer.callFunction`. */
class Xslt30TransformerCallFunctionMethod extends Method {
Xslt30TransformerCallFunctionMethod() {
this.getDeclaringType().hasQualifiedName("net.sf.saxon.s9api", "Xslt30Transformer") and
this.hasName("callFunction")
}
}
/** The method `net.sf.saxon.s9api.Xslt30Transformer.callTemplate`. */
class Xslt30TransformerCallTemplateMethod extends Method {
Xslt30TransformerCallTemplateMethod() {
this.getDeclaringType().hasQualifiedName("net.sf.saxon.s9api", "Xslt30Transformer") and
this.hasName("callTemplate")
}
}
/** The class `net.sf.saxon.s9api.XsltCompiler`. */
class TypeXsltCompiler extends Class {
TypeXsltCompiler() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltCompiler") }
}
/** The class `net.sf.saxon.s9api.XsltExecutable`. */
class TypeXsltExecutable extends Class {
TypeXsltExecutable() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltExecutable") }
}
/** The class `net.sf.saxon.s9api.XsltPackage`. */
class TypeXsltPackage extends Class {
TypeXsltPackage() { this.hasQualifiedName("net.sf.saxon.s9api", "XsltPackage") }
}
/** A data flow sink for unvalidated user input that is used in XSLT transformation. */
class XsltInjectionSink extends DataFlow::ExprNode {
XsltInjectionSink() {
exists(MethodAccess ma, Method m | m = ma.getMethod() and ma.getQualifier() = this.getExpr() |
ma instanceof TransformerTransform or
m instanceof XsltTransformerTransformMethod or
m instanceof Xslt30TransformerTransformMethod or
m instanceof Xslt30TransformerApplyTemplatesMethod or
m instanceof Xslt30TransformerCallFunctionMethod or
m instanceof Xslt30TransformerCallTemplateMethod
)
}
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` or `Reader` and
* `XMLStreamReader`, i.e. `XMLInputFactory.createXMLStreamReader(tainted)`.
*/
predicate xmlStreamReaderStep(ExprNode n1, ExprNode n2) {
exists(XmlInputFactoryStreamReader xmlStreamReader |
n1.asExpr() = xmlStreamReader.getSink() and
n2.asExpr() = xmlStreamReader
)
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` or `Reader` and
* `XMLEventReader`, i.e. `XMLInputFactory.createXMLEventReader(tainted)`.
*/
predicate xmlEventReaderStep(ExprNode n1, ExprNode n2) {
exists(XmlInputFactoryEventReader xmlEventReader |
n1.asExpr() = xmlEventReader.getSink() and
n2.asExpr() = xmlEventReader
)
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `XMLStreamReader` or
* `XMLEventReader` and `StAXSource`, i.e. `new StAXSource(tainted)`.
*/
predicate staxSourceStep(ExprNode n1, ExprNode n2) {
exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeStAXSource |
n1.asExpr() = cc.getAnArgument() and
n2.asExpr() = cc
)
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `InputStream` and `Document`,
* i.e. `DocumentBuilder.parse(tainted)`.
*/
predicate documentBuilderStep(ExprNode n1, ExprNode n2) {
exists(DocumentBuilderParse documentBuilder |
n1.asExpr() = documentBuilder.getSink() and
n2.asExpr() = documentBuilder
)
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `Document` and `DOMSource`, i.e.
* `new DOMSource(tainted)`.
*/
predicate domSourceStep(ExprNode n1, ExprNode n2) {
exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeDOMSource |
n1.asExpr() = cc.getAnArgument() and
n2.asExpr() = cc
)
}
/**
* A data flow configuration for secure processing feature that is enabled on `TransformerFactory`.
*/
private class TransformerFactoryWithSecureProcessingFeatureFlowConfig extends DataFlow2::Configuration {
TransformerFactoryWithSecureProcessingFeatureFlowConfig() {
this = "TransformerFactoryWithSecureProcessingFeatureFlowConfig"
}
override predicate isSource(DataFlow::Node src) {
exists(Variable v | v = src.asExpr().(VarAccess).getVariable() |
exists(TransformerFactoryFeatureConfig config | config.getQualifier() = v.getAnAccess() |
config.enables(configSecureProcessing())
)
)
}
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma |
sink.asExpr() = ma.getQualifier() and
ma.getMethod().getDeclaringType() instanceof TransformerFactory
)
}
override int fieldFlowBranchLimit() { result = 0 }
}
/** A `ParserConfig` specific to `TransformerFactory`. */
private class TransformerFactoryFeatureConfig extends ParserConfig {
TransformerFactoryFeatureConfig() {
exists(Method m |
m = this.getMethod() and
m.getDeclaringType() instanceof TransformerFactory and
m.hasName("setFeature")
)
}
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `Source` and `Transformer` or
* `Templates`, i.e. `TransformerFactory.newTransformer(tainted)` or
* `TransformerFactory.newTemplates(tainted)`.
*/
predicate newTransformerOrTemplatesStep(ExprNode n1, ExprNode n2) {
exists(MethodAccess ma, Method m | ma.getMethod() = m |
n1.asExpr() = ma.getAnArgument() and
n2.asExpr() = ma and
(
m.getDeclaringType() instanceof TransformerFactory and m.hasName("newTransformer")
or
m.getDeclaringType() instanceof TransformerFactory and m.hasName("newTemplates")
) and
not exists(TransformerFactoryWithSecureProcessingFeatureFlowConfig conf |
conf.hasFlowToExpr(ma.getQualifier())
)
)
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `Templates` and `Transformer`,
* i.e. `tainted.newTransformer()`.
*/
predicate newTransformerFromTemplatesStep(ExprNode n1, ExprNode n2) {
exists(MethodAccess ma, Method m | ma.getMethod() = m |
n1.asExpr() = ma.getQualifier() and
n2.asExpr() = ma and
m.getDeclaringType() instanceof TypeTemplates and
m.hasName("newTransformer")
)
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `Source` or `URI` and
* `XsltExecutable` or `XsltPackage`, i.e. `XsltCompiler.compile(tainted)` or
* `XsltCompiler.loadExecutablePackage(tainted)` or `XsltCompiler.compilePackage(tainted)` or
* `XsltCompiler.loadLibraryPackage(tainted)`.
*/
predicate xsltCompilerStep(ExprNode n1, ExprNode n2) {
exists(MethodAccess ma, Method m | ma.getMethod() = m |
n1.asExpr() = ma.getArgument(0) and
n2.asExpr() = ma and
m.getDeclaringType() instanceof TypeXsltCompiler and
(
m.hasName("compile") or
m.hasName("loadExecutablePackage") or
m.hasName("compilePackage") or
m.hasName("loadLibraryPackage")
)
)
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `XsltExecutable` and
* `XsltTransformer` or `Xslt30Transformer`, i.e. `XsltExecutable.load()` or
* `XsltExecutable.load30()`.
*/
predicate xsltExecutableStep(ExprNode n1, ExprNode n2) {
exists(MethodAccess ma, Method m | ma.getMethod() = m |
n1.asExpr() = ma.getQualifier() and
n2.asExpr() = ma and
m.getDeclaringType() instanceof TypeXsltExecutable and
(m.hasName("load") or m.hasName("load30"))
)
}
/**
* Holds if `n1` to `n2` is a dataflow step that converts between `XsltPackage` and
* `XsltExecutable`, i.e. `XsltPackage.link()`.
*/
predicate xsltPackageStep(ExprNode n1, ExprNode n2) {
exists(MethodAccess ma, Method m | ma.getMethod() = m |
n1.asExpr() = ma.getQualifier() and
n2.asExpr() = ma and
m.getDeclaringType() instanceof TypeXsltPackage and
m.hasName("link")
)
}

View File

@@ -1,100 +0,0 @@
import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking2
import SpringFrameworkLib
/**
* A taint-tracking configuration for unsafe user input
* that is used to construct and evaluate a SpEL expression.
*/
class ExpressionInjectionConfig extends TaintTracking::Configuration {
ExpressionInjectionConfig() { this = "ExpressionInjectionConfig" }
override predicate isSource(DataFlow::Node source) {
source instanceof RemoteFlowSource or
source instanceof WebRequestSource
}
override predicate isSink(DataFlow::Node sink) { sink instanceof ExpressionEvaluationSink }
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
expressionParsingStep(node1, node2) or
springPropertiesStep(node1, node2)
}
}
/**
* A sink for SpEL injection vulnerabilities,
* i.e. methods that run evaluation of a SpEL expression in a powerfull context.
*/
class ExpressionEvaluationSink extends DataFlow::ExprNode {
ExpressionEvaluationSink() {
exists(MethodAccess ma, Method m | m = ma.getMethod() |
m instanceof ExpressionEvaluationMethod and
getExpr() = ma.getQualifier() and
not exists(SafeEvaluationContextFlowConfig config |
config.hasFlowTo(DataFlow::exprNode(ma.getArgument(0)))
)
)
}
}
/**
* Holds if `node1` to `node2` is a dataflow step that parses a SpEL expression,
* i.e. `parser.parseExpression(tainted)`.
*/
predicate expressionParsingStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(MethodAccess ma, Method m | ma.getMethod() = m |
m.getDeclaringType().getAnAncestor*() instanceof ExpressionParser and
m.hasName("parseExpression") and
ma.getAnArgument() = node1.asExpr() and
node2.asExpr() = ma
)
}
/**
* A configuration for safe evaluation context that may be used in expression evaluation.
*/
class SafeEvaluationContextFlowConfig extends DataFlow2::Configuration {
SafeEvaluationContextFlowConfig() { this = "SpelInjection::SafeEvaluationContextFlowConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof SafeContextSource }
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma, Method m | m = ma.getMethod() |
m instanceof ExpressionEvaluationMethod and
ma.getArgument(0) = sink.asExpr()
)
}
override int fieldFlowBranchLimit() { result = 0 }
}
class SafeContextSource extends DataFlow::ExprNode {
SafeContextSource() {
isSimpleEvaluationContextConstructorCall(getExpr()) or
isSimpleEvaluationContextBuilderCall(getExpr())
}
}
/**
* Holds if `expr` constructs `SimpleEvaluationContext`.
*/
predicate isSimpleEvaluationContextConstructorCall(Expr expr) {
exists(ConstructorCall cc |
cc.getConstructedType() instanceof SimpleEvaluationContext and
cc = expr
)
}
/**
* Holds if `expr` builds `SimpleEvaluationContext` via `SimpleEvaluationContext.Builder`,
* e.g. `SimpleEvaluationContext.forReadWriteDataBinding().build()`.
*/
predicate isSimpleEvaluationContextBuilderCall(Expr expr) {
exists(MethodAccess ma, Method m | ma.getMethod() = m |
m.getDeclaringType() instanceof SimpleEvaluationContextBuilder and
m.hasName("build") and
ma = expr
)
}

View File

@@ -1,21 +1,6 @@
import java
import semmle.code.java.dataflow.DataFlow
/**
* Methods that trigger evaluation of an expression.
*/
class ExpressionEvaluationMethod extends Method {
ExpressionEvaluationMethod() {
getDeclaringType() instanceof Expression and
(
hasName("getValue") or
hasName("getValueTypeDescriptor") or
hasName("getValueType") or
hasName("setValue")
)
}
}
/**
* `WebRequest` interface is a source of tainted data.
*/
@@ -37,100 +22,6 @@ class WebRequestSource extends DataFlow::Node {
}
}
/**
* Holds if `node1` to `node2` is a dataflow step that converts `PropertyValues`
* to an array of `PropertyValue`, i.e. `tainted.getPropertyValues()`.
*/
predicate getPropertyValuesStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(MethodAccess ma, Method m | m = ma.getMethod() |
node1.asExpr() = ma.getQualifier() and
node2.asExpr() = ma and
m.getDeclaringType() instanceof PropertyValues and
m.hasName("getPropertyValues")
)
}
/**
* Holds if `node1` to `node2` is a dataflow step that constructs `MutablePropertyValues`,
* i.e. `new MutablePropertyValues(tainted)`.
*/
predicate createMutablePropertyValuesStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(ConstructorCall cc | cc.getConstructedType() instanceof MutablePropertyValues |
node1.asExpr() = cc.getAnArgument() and
node2.asExpr() = cc
)
}
/**
* Holds if `node1` to `node2` is a dataflow step that returns a name of `PropertyValue`,
* i.e. `tainted.getName()`.
*/
predicate getPropertyNameStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(MethodAccess ma, Method m | m = ma.getMethod() |
node1.asExpr() = ma.getQualifier() and
node2.asExpr() = ma and
m.getDeclaringType() instanceof PropertyValue and
m.hasName("getName")
)
}
/**
* Holds if `node1` to `node2` is a dataflow step that converts `MutablePropertyValues`
* to a list of `PropertyValue`, i.e. `tainted.getPropertyValueList()`.
*/
predicate getPropertyValueListStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(MethodAccess ma, Method m | m = ma.getMethod() |
node1.asExpr() = ma.getQualifier() and
node2.asExpr() = ma and
m.getDeclaringType() instanceof MutablePropertyValues and
m.hasName("getPropertyValueList")
)
}
/**
* Holds if `node1` to `node2` is one of the dataflow steps that propagate
* tainted data via Spring properties.
*/
predicate springPropertiesStep(DataFlow::Node node1, DataFlow::Node node2) {
createMutablePropertyValuesStep(node1, node2) or
getPropertyNameStep(node1, node2) or
getPropertyValuesStep(node1, node2) or
getPropertyValueListStep(node1, node2)
}
class PropertyValue extends RefType {
PropertyValue() { hasQualifiedName("org.springframework.beans", "PropertyValue") }
}
class PropertyValues extends RefType {
PropertyValues() { hasQualifiedName("org.springframework.beans", "PropertyValues") }
}
class MutablePropertyValues extends RefType {
MutablePropertyValues() { hasQualifiedName("org.springframework.beans", "MutablePropertyValues") }
}
class SimpleEvaluationContext extends RefType {
SimpleEvaluationContext() {
hasQualifiedName("org.springframework.expression.spel.support", "SimpleEvaluationContext")
}
}
class SimpleEvaluationContextBuilder extends RefType {
SimpleEvaluationContextBuilder() {
hasQualifiedName("org.springframework.expression.spel.support",
"SimpleEvaluationContext$Builder")
}
}
class WebRequest extends RefType {
WebRequest() { hasQualifiedName("org.springframework.web.context.request", "WebRequest") }
}
class Expression extends RefType {
Expression() { hasQualifiedName("org.springframework.expression", "Expression") }
}
class ExpressionParser extends RefType {
ExpressionParser() { hasQualifiedName("org.springframework.expression", "ExpressionParser") }
}