Example

0x01 Getting Started

Create Database for java:

codeql database create test4fun --language="java" --command="mvn clean install --file pom.xml" --source-root=D:\Code\Java\CodeQL\Demo\test1

Get Familiar With This Weird And Wicked Declarative Program🤧

Here is an example:Finding if statements which have an empty then block

import java

from IfStmt ifStmt, BlockStmt block
where
  block = ifStmt.getThen() and
  block.getNumStmt() = 0
select ifStmt, "This isf-statement has an empty then-block"
  • Predicate Wrap

import java

predicate isEmpty(BlockStmt block) {
  block.getNumStmt() = 0
}

from IfStmt ifStmt
where
  isEmpty(ifStmt.getThen())
select ifStmt
  • Class Wrap

import java

class EmptyBlock extends BlockStmt{
  EmptyBlock(){
    this.getNumStmt() = 0
  }
}
from IfStmt ifStmt
where
  ifStmt.getThen() instanceof EmptyBlock
select ifStmt

Or Use It In This Way

import java

class EmptyBlock extends BlockStmt{
  EmptyBlock(){
    this.getNumStmt() = 0
  }
}
from IfStmt ifStmt, EmptyBlock block
where
  ifStmt.getThen() = block
select ifStmt

0x02 Apprentice Lab

Unsafe deserialization in Apache Struts —— CVE-2017-9805

Download and unzip👉 apache_struts_cve_2017_9805.zip database

Finding XML deserialization

XStream is a Java framework for serializing Java objects to XML used by Apache Struts. It provides a method XStream.fromXML for deserializing XML to a Java object. By default, the input is not validated in any way, and is vulnerable to remote code execution exploits. In this section, we will identify calls to fromXML in the codebase.

  1. 查找程序中的所有方法调用

import java

from MethodAccess call
select call
  1. 找到所有方法调用及其对应的方法声明

import java

from MethodAccess call, Method method
where call.getMethod() = method
select call, method
  1. 找到程序中所有调用fromXML的地方

import java

from MethodAccess call, Method method
where 
  call.getMethod() = method and
  method.getName() = "fromXML"
select call, method

可以简化为

import java

from MethodAccess fromXML
where 
  fromXML.getMethod().getName() = "fromXML"
select fromXML
  1. 找出调用fromXML方法的第一个参数

import java

from MethodAccess fromXML, Expr arg
where 
  fromXML.getMethod().getName() = "fromXML" and
  arg = fromXML.getArgument(0)
select fromXML, arg
  1. 使用谓词包装

import java

predicate isXMLDeserialized(Expr arg) {
  exists(MethodAccess fromXML | 
    fromXML.getMethod().getName() = "fromXML" and
    arg = fromXML.getArgument(0)  
  )
}

from Expr arg
where isXMLDeserialized(arg)
select arg

Find the implementations of the toObject method from ContentTypeHandler

  1. 创建一个ContentTypeHandler类,找到org.apache.struts2.rest.handler.ContentTypeHandler接口

import java

class ContentTypeHandler extends RefType {
  ContentTypeHandler() {
      this.hasQualifiedName("org.apache.struts2.rest.handler", "ContentTypeHandler")
  }
}
  1. 创建一个ContentTypeHandlerToObject类,识别org.apache.struts2.rest.handler.ContentTypeHandler子类/实现类的toObject的方法

class ContentTypeHandlerToObject extends Method {
  ContentTypeHandlerToObject() {
    this.getName() = "toObject" and
    this.getDeclaringType().getASupertype() instanceof ContentTypeHandler
  }
}
  1. toObject方法应将第一个参数视为不受信任的用户输入。查询toObject方法的第一个参数

from ContentTypeHandlerToObject toObject
select toObject.getParameter(0)

Unsafe XML deserialization

(英文写的很通俗易懂,就直接搬运了)

We have now identified (a) places in the program which receive untrusted data and (b) places in the program which potentially perform unsafe XML deserialization. We now want to tie these two together to ask: does the untrusted data ever flow to the potentially unsafe XML deserialization call?

In program analysis we call this a data flow problem. Data flow helps us answer questions like: does this expression ever hold a value that originates from a particular other place in the program?

We can visualize the data flow problem as one of finding paths through a directed graph, where the nodes of the graph are elements in program, and the edges represent the flow of data between those elements. If a path exists, then the data flows between those two nodes.

Consider this example Java method:

int func(int tainted) {
   int x = tainted;
   if (someCondition) {
     int y = x;
     callFoo(y);
   } else {
     return x;
   }
   return -1;
}

The data flow graph for this method will look something like this:

This graph represents the flow of data from the tainted parameter. The nodes of graph represent program elements that have a value, such as function parameters and expressions. The edges of this graph represent flow through these nodes.

CodeQL for Java provides data flow analysis as part of the standard library. You can import it using semmle.code.java.dataflow.DataFlow. The library models nodes using the DataFlow::Node CodeQL class. These nodes are separate and distinct from the AST (Abstract Syntax Tree, which represents the basic structure of the program) nodes, to allow for flexibility in how data flow is modeled.

/**
* @kind path-problem
*/
import java
import semmle.code.java.dataflow.DataFlow
import DataFlow::PathGraph

class ContentTypeHandler extends RefType {
  ContentTypeHandler() {
    this.hasQualifiedName("org.apache.struts2.rest.handler", "ContentTypeHandler")
  }
}

class ContentTypeHandlerToObject extends Method {
  ContentTypeHandlerToObject() {
    this.getName() = "toObject" and
    this.getDeclaringType().getASupertype() instanceof ContentTypeHandler
  }
}


predicate isXMLDeserialized(Expr arg) {
  exists(MethodAccess fromXML | 
    fromXML.getMethod().getName() = "fromXML" and
    arg = fromXML.getArgument(0)  
  )
}


class StrutsUnsafeDeserializationConfig extends DataFlow::Configuration {
  StrutsUnsafeDeserializationConfig() { this = "StrutsUnsafeDeserializationConfig" }
  override predicate isSource(DataFlow::Node source) {
    exists(ContentTypeHandlerToObject toObject |
      source.asParameter() = toObject.getParameter(0)
    )
  }
  override predicate isSink(DataFlow::Node sink) {
    exists(Expr arg |
      isXMLDeserialized(arg) and
      sink.asExpr() = arg
    )
  }
}

from StrutsUnsafeDeserializationConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink, source, sink, "Unsafe XML deserialization"

0x03 CodeQL Recipe for Java

RefType

getACallable() 获取所有可以调用方法(包括构造方法)

getAMember() 获取所有成员,其中包括调用方法,字段和内部类

getAField() 获取所有字段

getAMethod() 获取所有方法

getASupertype() 获取父类

getAnAncestor() 获取所有的父类相当于递归的getASupertype*()

hasQualifiedName(packageName, className) 标识具有给定包名和类名的类

Method

getName() 获取类名

getDeclaringType() 获取方法的声明类型

getParameter(int index) 获取索引为index的参数(索引从0开始)

Last updated