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 ifStmtClass Wrap
import java
class EmptyBlock extends BlockStmt{
EmptyBlock(){
this.getNumStmt() = 0
}
}
from IfStmt ifStmt
where
ifStmt.getThen() instanceof EmptyBlock
select ifStmtOr 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 ifStmt0x02 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.
查找程序中的所有方法调用
import java
from MethodAccess call
select call找到所有方法调用及其对应的方法声明
import java
from MethodAccess call, Method method
where call.getMethod() = method
select call, method找到程序中所有调用
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找出调用
fromXML方法的第一个参数
import java
from MethodAccess fromXML, Expr arg
where
fromXML.getMethod().getName() = "fromXML" and
arg = fromXML.getArgument(0)
select fromXML, arg使用谓词包装
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 argFind the implementations of the toObject method from ContentTypeHandler
创建一个
ContentTypeHandler类,找到org.apache.struts2.rest.handler.ContentTypeHandler接口
import java
class ContentTypeHandler extends RefType {
ContentTypeHandler() {
this.hasQualifiedName("org.apache.struts2.rest.handler", "ContentTypeHandler")
}
}创建一个
ContentTypeHandlerToObject类,识别org.apache.struts2.rest.handler.ContentTypeHandler子类/实现类的toObject的方法
class ContentTypeHandlerToObject extends Method {
ContentTypeHandlerToObject() {
this.getName() = "toObject" and
this.getDeclaringType().getASupertype() instanceof ContentTypeHandler
}
}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
Was this helpful?