Discovering _ CVE-2022–22980 real exploitable path
🛣️

Discovering _ CVE-2022–22980 real exploitable path

📅 [ Archival Date ]
Oct 19, 2022 11:20 PM
🏷️ [ Tags ]
CVE-2022–22980CodeQL
✍️ [ Author ]
💣 [ PoC / Exploit ]
image

Introduction

In this post, I will walk you through a journey that starts analyzing CVE-2022–22980 and extracting a pattern of this vulnerability to query a codebase to discover the true exploitation path.

While many vulnerability scanning tools are powerful in detecting vulnerable components in an application, some of them fail to prove whether the vulnerability is exploitable by a threat.

As a powerful ally for this task, there is CodeQL. It is a semantic code analysis engine open source from GitHub that lets query your codebase as it were data. Thus, it is a crucial tool that allows us to search for a pattern of vulnerability in the entire source code.

In the creation process, it is possible to refine the query until just exploitable paths are discovered, which will be demonstrated in this article.

Analysis of CVE-2022–22980

While reading some security vulnerabilities bulletins, CVE-2022–22980 caught my attention. It treats about a fix in an Expression Language injection in Spring Data MongoDB that could lead to remote code execution.

So, reviewing the announcement provided by VMWare some points were identified that could lead to a vulnerable scenario.

A repository query method is annotated with @Query or @Aggregation

The annotated query or aggregation value/pipeline contains SpEL parts using the parameter placeholder syntax within the expression

The user supplied input is not sanitized by the application

Accomplishing these conditions and using the Spring Data MongoDB in both 3.4.0, 3.3.0 to 3.3.4 and older unsupported versions, the application is prone to vulnerability. Thus, the next step will turn these conditions into a pattern and build a CodeQL query to search the entire codebase.

Building a CodeQL query

The first step in building this query is to determine which user inputs are used by the application. So RemoteFlowSources is a good point for this task. It is a Class defined by CodeQL that maps a data flow source of remote user input.

So in the first part of this query, we needed to do the imports and define that sources are equal to remote flow sources.

/**
 *@kind path-problem
 */
import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.DataFlow
import DataFlow::PathGraphclass MyConf extends DataFlow::Configuration {
  MyConf() { this = "MyConf" }

  override predicate isSource(DataFlow::Node source) {
    exists(RemoteFlowSource remote | source.asParameter() = remote.asParameter())
  }

Once defined which are the sources is time to build a sink part of the query. In this part, the conditions of exploitability must be met. Thus, three variables were defined to conduct this phase. One for methods that have annotations assigned, the other for the names of the annotations, and the last one for finding access to methods with assigned annotations.

After defining the variables, it is necessary to perform conditional operations that find the correct sink. First, it was compared if the method annotation was equal to the annotation defined by us, then it was compared if the annotation was equal to both Query or Aggregation, then a search via regex in annotation for values that contain ?[0-9], finally it is defined that the access to the method must be equal to the method defined by us and the argument of this access must be equal to our sink.

override predicate isSink(DataFlow::Node sink) {
    exists(Method c, Annotation ann, MethodAccess ma | ann = c.getAnAnnotation() and (ann.toString() = "Query" or ann.toString() = "Aggregation") and ann.getAValue().toString().regexpMatch(".*\\?[0-9].*")
and ma.getMethod() = c and ma.getAnArgument()
 = sink.asExpr())
  }

At the end of the query, the portion that connects all the points of our constructed query was defined. The final query looks like this:

/**
 *@kind path-problem
 */
import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.DataFlow
import DataFlow::PathGraphclass MyConf extends DataFlow::Configuration {
  MyConf() { this = "MyConf" }

  override predicate isSource(DataFlow::Node source) {
    exists(RemoteFlowSource remote | source.asParameter() = remote.asParameter())
  }override predicate isSink(DataFlow::Node sink) {
    exists(Method c, Annotation ann, MethodAccess ma | ann = c.getAnAnnotation() and (ann.toString() = "Query" or ann.toString() = "Aggregation") and ann.getAValue().toString().regexpMatch(".*\\?[0-9].*")
and ma.getMethod() = c and ma.getAnArgument()
 = sink.asExpr())
  }
}from MyConf conf, DataFlow::PathNode source, DataFlow::PathNode sink
where conf.hasFlowPath(source, sink)
select sink, source, sink, "SpEL Injection found! - CVE-2022-22980"

Executing the query in CodeQL

For this phase, we used the vulnerable public application designed for this security issue, which will serve as our codebase. Thus, the CodeQL database was created from the codebase that was defined.

codeql database create cveanalysis --language=java

Once the database is created, it’s time to import it into VSCode’s CodeQL extension to run our query.

image

VSCode after database import and query filled

After selecting the recently uploaded database, it is time to go to the VSCode workspace for CodeQL in the Java tab and edit the default template with our query. Finally can run our query in the codebase.

image

Results of query execution

It is the possible view that there is a real path exploitable for the vulnerability in this codebase. It is the possible view that there is a real exploitable path to the vulnerability in this codebase. The results give us the source, drain, and intermediate steps if any. In this case, there is a simple path between the source and sink.

image

source and sink

Validating the Exploitable Path

As identified in prior steps this vulnerability receives a parameter in its /test endpoint and passes it to the vulnerable function. As identified in the previous steps, this vulnerability receives a POST parameter on its /testendpoint and passes it to the vulnerable function. So by running the following command, the vulnerability is achieved:

image

cURL command with malicious payload

With this, we can constant that the file was created in the temporary directory and the remote code execution was achieved.

image

After and before code execution

Conclusion

It is also important to determine if a vulnerability is exploitable in your environment once this influence a Risk Management process.

More about CVE-2022–22980 on this previous post 👇