Skip to content

Latest commit

 

History

History
288 lines (191 loc) · 14.4 KB

README.md

File metadata and controls

288 lines (191 loc) · 14.4 KB

CodeQL workshop for Java: Finding a SQL injection

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

Contents

Prerequisites and setup instructions

On your local machine

Please complete this section before the workshop, if possible.

Installation

Setup steps

  • Import the CodeQL database to be used in the workshop:
    • Right-click on the xwiki-platform-CVE-2021-21380.zip file in the Explorer view and select the command CodeQL: Set Current Database.
    • The database will show up in the CodeQL databases view reachable from the QL icon on the Activity Bar.
  • Install the dependencies for analyzing Java code and to run the tests for the exercises and solutions.
    • From the Command Palette (Cmd/Ctrl+Shift+P), search for and run the command CodeQL: Install Pack Dependencies.
    • At the top of your VS Code window, type github in the box to filter the list.
    • Check the box next to cve-2021-21380-exercises, cve-2021-21380-exercises-tests, cve-2021-21380-solutions, and cve-2021-21380-solutions-tests.
    • Click OK/Enter.
  • Validate everything works as expected by running the tests for the solutions.
    • Open the view testing and press run tests (play icon is shown when hovering solutions) next to the solutions item in the tree.

Workshop

Learnings

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:

  • How to reason about syntactic information.
  • How to reason about semantic information.
  • Explore the QL language support for Java to express patterns.
  • Explore how to reuse and extend existing modelling.
  • Use multiple building blocks to compose the final query.

Problem statement

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:

  • Identify the source of intrusted information and model it in QL.
  • Identify the sink, the method executing SQL queries, and model it in QL.
  • Combine the above solutions to determine if there is a flow of information between the source and the sink using taint tracking.

Exercises

In the first few exercises we will reason about syntactic information, using the Abstract Syntax Tree (AST), to identify:

  • the method described in the security advisory to build understanding of the vulnerability
  • parameters that contain untrusted data, our sources
  • method calls that accept SQL statements, our sinks

Exercise 1

Find all methods with the name getAverageRating and its declaring type in the program by completing the query exercise1.ql

Hints
  • The java module provides a class Method to reason about methods in a program.
  • The class Method provides the member predicates getName and hasName to reason about the name of a method.
  • The class Method provides the member getDeclaringType to reason about the type that declares the method.

A solution can be found in the query exercise1.ql

Intermezzo 1

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:

  • XWiki uses a component oriented design to allow for extensions and customizations.
  • The vulnerable method is part of a component.
  • A component consist of an interface, annotated with Role, and an implementation annotated with Component.
  • A component that extends ScriptService are made accessible to wiki pages through scripting.

Exercise 2

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.

Hints
  • The /class domain type/, the intersection of its super types, can be accessed using the keyword this in the /characteristic predicate/.
  • The Class class provides a method getAnAnnotation to get associated annotations.
  • The Annotation class provides the getType member predicate to reason about its type.
  • The Type class provides the member predicates getName and hasName to reason about the name of a type.
  • The 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

Exercise 3

Find all the components that implement the ScriptService interface by completing the query excercise3.ql

Hints
  • The 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

Intermezzo 2

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.

Exercise 4

Find all methods calls to the method search and identify its declaring type by completing the query exercise4.ql

Hints
  • The MethodCall type provide us with the means to reason about method calls.
  • The 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

Exercise 5

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

Hints
  • 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

Intermezzo 3

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:

  1. The module DataFlow supports value preserving flow of information.
  2. The module 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.

Exercise 6

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.

Hints
  • 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

Exercise 7

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

Hints
  • 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

Exercise 8

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

What's next?

  • The query includes a module to model parts of the XWiki framework. Refactor this into its own module file and use it in your query.
  • We limited our source of untrusted data to script service components. Look at how to expand this with other sources.
  • For the sink we limited ourselves to direct usage of the 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.
  • Use the definition of sources to find interesting uses of untrusted data with the query 'Untrusted data passed to external API '