
A CodeQL workshop covering CVE-2021-21380
In this workshop we will use syntactical and semantic reasoning to find a SQL injection in the XWiki platform's rating component documented by CVE-2021-21380
Please complete this section before the workshop, if possible.
Install Visual Studio Code.
Install the CodeQL extension for Visual Studio Code.
You do not need to install the CodeQL CLI: the extension will handle this for you.
Clone this repository:
git clone https://github.com/advanced-security/codeql-workshop-cve-2021-21380.git
xwiki-platform-CVE-2021-21380.zip file in the Explorer view and select the command CodeQL: Set Current Database.Cmd/Ctrl+Shift+P), search for and run the command CodeQL: Install Pack Dependencies.github in the box to filter the list.cve-2021-21380-exercises, cve-2021-21380-exercises-tests, cve-2021-21380-solutions, and cve-2021-21380-solutions-tests.The workshop is split into several exercises introducing the QL language support for Java and ends with a final query to find the known SQL injection. In these exercises you will learn:
In this workshop we will look for known SQL injection vulnerabilities in the XWiki Platform's ratings API component. Such vulnerabilities can occur in applications when information that is controlled by an external user makes its way to application code that insecurely construct a SQL query and executes it.
The known SQL injection discussed in this workshop is reviewed in GHSA-79rg-7mv3-jrr5 in GitHub Advisory Database. To find the SQL injection, and possible variants, we are going to the following sub-problems:
In the first few exercises we will reason about syntactic information, using the Abstract Syntax Tree (AST), to identify:
Find all methods with the name getAverageRating and its declaring type in the program by completing the query exercise1.ql
java module provides a class Method to reason about methods in a program.Method provides the member predicates getName and hasName to reason about the name of a method.Method provides the member getDeclaringType to reason about the type that declares the method.A solution can be found in the query exercise1.ql
A solution to exercise 1 returns a list of methods. Some of which are defined in an interface called RatingsManager and some which are defined in the classes AbstractRatingsManager and RatingsScriptService.
From the information returned by the query and XWiki component documentation we can deduce that:
Role, and an implementation annotated with Component.ScriptService are made accessible to wiki pages through scripting.Find all the classes annotated with the annotation Component by completing the query exercise2.ql.
Note that the fully qualified name of the annotation's type is org.xwiki.component.annotation.Component.
this in the /characteristic predicate/.Class class provides a method getAnAnnotation to get associated annotations.Annotation class provides the getType member predicate to reason about its type.Type class provides the member predicates getName and hasName to reason about the name of a type.RefType class, representing classes and interfaces, provides the member predicates getQualifiedName and hasQualifiedName to reason about the fully qualified name of the reftype.A solution can be found in the query exercise2.ql
Find all the components that implement the ScriptService interface by completing the query excercise3.ql
Class type provides the member predicate getASuperType to reason about a class its super types, that is types it extends or implements.A solution can be found in the query exercise3.ql
At this point we have syntactically identified the methods that can be called by a user and whose parameters we will consider sources of untrusted data further on in the workshop.
In the next exercise we are going to investigate and identify possible sinks. From the results of exercise 1 we can deduce that one of the implementations calls the method getAverageRatingFromQuery. Using a similar query we can find the declaring types of getAverageRatingFromQuery which allows us to establish that the implementation in the class AbstractRatingsManager constructs a SQL statement that is passed to the search method.
The search method is implemented in a dependency and thus its implementation is not available. In the next exercises we are going to use the available type information to identify this search method call and its declaring type.
Find all methods calls to the method search and identify its declaring type by completing the query exercise4.ql
MethodCall type provide us with the means to reason about method calls.MethodAcccess type provides the member predicate getMethod to reason about the target of a method call.A solution can be found in the query exercise4.ql
Find all the method calls to methods declared by the interface XWikiStorageInterface (with the qualified name com.xpn.xwiki.store.XWikiStoreInterface) by completing the query exercise5.ql
The instanceof keyword can be used to state that a value belongs to the set of values represented by a type.
For example, to identify all the calls to interface methods:
import java
from MethodCall ma
where ma.getMethod.getDeclaringType() instanceof Interface
select ma
A solution can be found in the query exercise5.ql
At this point we have syntactically described possible sources and sink using QL. To determine if information flows between these points in the program we are going to semantically analyze the program using taint tracking. The standard libraries of the languages we support provide two data flow mechanism:
DataFlow supports value preserving flow of information.TaintTracking supports flow of information even if values are modified.The latter is of interest, because in injection vulnerability such as SQL injection it is common for untrusted data to become part of a larger statement that is acted upon.
In this workshop we are going to reuse an existing SQL injection taint tracking configuration and extend it with our modelled sources and sinks to find the SQL injection. To understand how we can extend the configuration we start with looking at the definition of the configuration below.
class QueryInjectionFlowConfig extends TaintTracking::Configuration {
QueryInjectionFlowConfig() { this = "SqlInjectionLib::QueryInjectionFlowConfig" }
override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof QueryInjectionSink }
override predicate isSanitizer(DataFlow::Node node) {
node.getType() instanceof PrimitiveType or
node.getType() instanceof BoxedType or
node.getType() instanceof NumberType
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
any(AdditionalQueryInjectionTaintStep s).step(node1, node2)
}
}
Both the isSource and isSink predicate used by the configuration to identify those program location use the instanceof keyword that we have seen before. Both the RemoteFlowSource and QueryInjectionSink classes are abstract classes.
This is a common pattern that you will encounter in the standard libraries and this pattern allow us to extend the set of values represented by the RemoteFlowSource and QueryInjectionSink classes.
A QL class extending an abstract class does not refine the set of value represented by the super class, but adds the values represented by the subclass to the superclass.
In the next exercises we are going to implement these subclasses and construct our final query.
Extend the RemoteFlowSource's value set with the parameters of the public methods of the component classes identified in exercise 3 by completing the query exercise6.ql.
The instanceof keyword can be used to state that a value belongs to the set of values represented by a type.
For example, to identify all the calls to interface methods:
import java
from MethodCall ma
where ma.getMethod.getDeclaringType() instanceof Interface
select ma
A solution can be found in the query exercise6.ql
Extend the QueryInjectionSink's value set with the arguments of calls to the methods of the storage interface identified in exercise 5 by completing the query exercise7.ql
The exists formula allows for the introduction of temporary variable that can be reasoned about in the scope of the exists.
The following example uses the exists expression to reduce the set of methods to methods that are called.
from Method m
where m.hasName("foo") and exists(MethodCall ma | ma.getMethod() = m)
select m
The MethodCall class provides the member predicate getQualifer to reason about the qualifier of the method access.
A solution can be found in the query exercise7.ql
Combine your solutions from the previous exercise into a final solution by completing by completing the query exercise8.ql.
A solution can be found in the query exercise8.ql
XWikiStoreInterface. Expand the sink to include direct uses of implementation of the interface. The Class class provides the member predicate extendsOrImplements and the Method class provides the member predicate overridesOrInstantiates that may be of help.