CodeQL使用

0x00 环境搭建

CodeQL技术让人眼前一亮,它将代码当作数据来处理,使用类似SQL的语法来查询代码的AST和数据流。本文简单介绍下该工具的使用和基本知识,当作备忘录。

该工具的特点:

  • 将代码当作数据,用SQL查询。
  • 支持多种语言(暂时不支持PHP)
  • 数据流/污点追踪,AST语法树都有。

研究还是看官方文档靠谱,我看过的文档记录下来。

入门QL语法:https://help.semmle.com/QL/learn-ql/introduction-to-ql.html

QL语法手册:https://help.semmle.com/QL/ql-handbook/predicates.html
JAVA QL语法支持:https://help.semmle.com/QL/learn-ql/java/introduce-libraries-java.html.

简单步骤:

需要下载和clone的资源:

0x01 初步使用

CodeQL帮助我们进行了语法树和数据流分析,现在只需要专注于查询语句的写法即可。

例如,通过QL语法可以快速查询出空block:

1
2
3
4
5
import java

from Block b
where b.getNumStmt() = 0
select b, "This is an empty block."

0x02 WebGoat实际测试

最赞的是数据流分析:单文件数据流(local data flow),跨文件数据流(global data flow)。
https://help.semmle.com/QL/learn-ql/java/dataflow.html

选取WebGoat项目来看看Codeql的数据流分析效果。

1. codeql生成数据库

webgoat v.8.0.0.M21之后不支持JDK8,需要checkout换分支。

1
2
git checkout tags/v8.0.0.M21
codeql database create webgoat --language=java

测试项目需要可以mvn编译通过,实际编译脚本为:

1
mvn clean package -f "pom.xml" -B -V -e -Dfindbugs.skip -Dcheckstyle.skip -Dpmd.skip=true -Denforcer.skip -Dmaven.javadoc.skip -DskipTests -Dmaven.test.skip.exec -Dlicense.skip=true

2. QL的SQL注入查询

使用QL库的JAVA安全样例,CWE-089SqlTainted代码,直接跑:

1
2
3
4
5
6
7
8
import semmle.code.java.Expr
import semmle.code.java.dataflow.FlowSources
import SqlInjectionLib
import DataFlow::PathGraph

from QueryInjectionSink query, DataFlow::PathNode source, DataFlow::PathNode sink
where queryTaintedBy(query, source, sink)
select query, source, sink, "Query might include code from $@.", source.getNode(), "this user input"

直接追进去可以看到,定义了标准的source、sink、sanitizer流程~

1
2
3
4
5
6
7
8
9
10
11
private class QueryInjectionFlowConfig extends TaintTracking::Configuration { // 继承了taint tracking,就是全局污点跟踪~ @angelwhu
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
}
}

其中定义的src需要完善,没有很好的支持Spring Boot的RestController写法。

source、sink样例

成功找出了3个SQL注入,效果可以,优化的空间也有许多。测试代码后续更新到github上。

0x03 Log4j2漏洞挖掘

代码环境:https://github.com/didi/AgileTC/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java

// from MethodAccess call, Method method
// where
// ( method.hasName("info") or method.hasName("debug") or method.hasName("trace")
// or method.hasName("warn") or method.hasName("error")
// )
// and method.getDeclaringType().getAnAncestor().hasQualifiedName("org.slf4j", "Logger")

// and call.getMethod() = method
// select call, call.getCompilationUnit(), call.getArgument(0)
import semmle.code.java.frameworks.spring.SpringController

from SpringRequestMappingMethod route
// where method.getDeclaringType().getAnAncestor().hasQualifiedName("org.slf4j", "Logger")

select route, route.getARequestParameter()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
@kind path-problem
*/

import java
import semmle.code.java.dataflow.FlowSources
import DataFlow::PathGraph
import semmle.code.java.frameworks.spring.SpringController


class Log4j2Method extends Method{
Log4j2Method(){
this.getDeclaringType().getAnAncestor().hasQualifiedName("org.slf4j", "Logger")
and
(
( this.hasName("info") or this.hasName("debug") or this.hasName("trace")
or this.hasName("warn") or this.hasName("error") ))
}
}

// 连接GetSet方法:https://xz.aliyun.com/t/10852#toc-9
class GetSetTaintStep extends TaintTracking::AdditionalTaintStep{
override predicate step(DataFlow::Node src, DataFlow::Node sink){
exists(MethodAccess ma |
(
ma.getMethod() instanceof GetterMethod or ma.getMethod() instanceof SetterMethod or ma.getMethod().getName().matches("get%") or ma.getMethod().getName().matches("set%"))
and
src.asExpr() = ma.getQualifier()
and sink.asExpr() = ma
)
}
}

class MyTaintTrackingConfiguration extends TaintTracking::Configuration {
MyTaintTrackingConfiguration() { this = "MyTaintTrackingConfiguration" }

override predicate isSource(DataFlow::Node source) {
// exists(Method m, Parameter p |
// m.getAnAnnotation().getType().hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping") and
// m.hasAnnotation() and
// m.getAParameter() = p and
// source.asParameter() = p and
// p.getType().hasName("HttpServletRequest")
// )
// or
exists( SpringRequestMappingMethod route| source.asParameter()=route.getARequestParameter() )
or
source instanceof RemoteFlowSource

}

override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess call |
call.getMethod() instanceof Log4j2Method and sink.asExpr() = call.getArgument(0)
)
}

// override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
// }
}


from MyTaintTrackingConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select source.getNode(), source, sink, "source are"

0x04 参考文档

Code学习笔记
CodeQL 提升篇
codeql学习——污点分析
CodeQL CTF竞赛
CodeQL CTF竞赛答案

code java官方指导

Use CodeQL to Find CVE 2020 9297

CVE-2018-11776: How to find 5 RCEs in Apache Struts with CodeQL
OGNL injection in Apache Struts: Discovering exploits with taint tracking

Codeql整体介绍
Codeql使用介绍

文章作者: angelwhu
文章链接: https://www.angelwhu.com/paper/2019/12/30/CodeQL-introduction/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 angelwhu_blog