上篇讲解啦codeql的基础开发环境配置, 本章主要讲解基础语法
创建数据库
codeql database create [编译后的数据库生成路径] --language=java --command="mvn clean install --file pom.xml" --source-root="./apps/WebGoat"
-l,--language=<lang> 创建数据库的语言
-s,--source-root=<dir> 项目的源代码路径,默认为当前路径
-j,--threads=<num> 生成数据库使用的线程数,默认为1
-M,--ram=<MB> 使用多大内存执行生成命令
-c.--command=<command> 构建项目使用的命令,如maven项目使用mvn clean package等
--overwrite 覆盖之前生成的数据库,如果不加上该命令,若存在同名数据库,则报错。
非编译型语言
# python项目
codeql database create --language=python <output-folder>/python-database
# 指定优先提取器创建
codeql database create --language=go testdb --search-path ../build/codeql-extractor-go
编译型语言
# 使用以下内容构建的 C/C++ 项目make
codeql database create cpp-database --language=cpp --command=make
# CODEQL_EXTRACTOR_GO_BUILD_TRACING=on使用环境变量构建的 Go 项目
CODEQL_EXTRACTOR_GO_BUILD_TRACING=on codeql database create go-database --language=go
# 使用 Gradle 构建的 Java 项目
codeql database create java-database --language=java --command='gradle --no-daemon clean test'
# 使用 Maven 构建的 Java 项目
codeql database create java-database --language=java --command='mvn clean install'
# 自定义编译脚本,创建数据库
codeql database create new-database --language=<language> --command='./scripts/build.sh'
执行分析
codeql database analyze --ram=8000 --threads=4 --format="sarif-latest" --output="./results/sql.json" ./databases/java-sec-database ./CWE-089/SqlTainted.ql
解释:
--ram=8000 & --threads=4 分析使用的内存和线程数
--format=<format> (必填) 输出结果的格式,如sarif-latest可以输出json格式,csv输出csv格式
--output="./results/sql.json" (必填) 输出结果路径
./databases/java-sec-database (必填) 分析的数据库路径
./CWE-089/SqlTainted.ql (必填) 使用的查询语句
测试
# 测试单个文件:
codeql dataset check testdb/db-go
codeql query run ../ql/test/test.ql --database=testdb --output=test.bqrs --search-path ..
codeql bqrs decode test.bqrs --format=csv --output=test.csv
diff -w -u <(sort test.csv) expected.csv
# 测试单个文件:
codeql database analyze ../basic/basic.testproj --format=dot nodeGraph.ql --output=outdir --rerun
dot -Tpdf -O outdir/test-plot-cfg.dot
open outdir/test-plot-cfg.dot.pdf
# 测试所有自带安全ql
codeql database analyze python_test ../../workenv/codeql/lib/python/ql/src/Security/* --format=sarif-latest --output=python-results.json
# 测试ql/python/ql/src/Functions/下所有ql文件
codeql database analyze <python-database> ../ql/python/ql/src/Functions/ --format=sarif-latest --output=python-analysis/python-results.json
基础语法
非污点跟踪
MethodAccess :调用的方法表达式
Method : class中的方法
getCaller :方法调用所在的方法,换句话说是哪个方法体中调用的
getCallee: 被调用的方法
hasName(param):限定名称
getDeclaringType():获取此变量的类型
hasQualifiedName("package","class"):限定类名
样例:
/**
* @name JNDI lookup with user-controlled name
* @description Performing a JNDI lookup with a user-controlled name can lead to the download of an untrusted
* object and to execution of arbitrary code.
* @kind problem
* @problem.severity error
* @precision high
* @id java/jndi-injection
* @tags security
*
*/
import java
from MethodAccess methodAccess
where methodAccess.getMethod().hasName("lookup") and methodAccess.getMethod().getDeclaringType().hasQualifiedName("javax.naming", "Context")
select methodAccess,methodAccess.getCaller().getName()
/*
注释
在使用命令行解析或vscode的时候,必须包含以下两个注释:(参考query-metadata-style-guide)
@kind
定义查询的类型,可以定义的类型为以下3个
problem:必须两列或其倍列,结构:element,string
path-problem,必须包含四列,结构:element, source, sink, string,后续查询必须以element,string结构
metric
@id
定义该查询的唯一标识,应以语言为开头,支持的语言如下:
C and C++: cpp
C#: cs
Go: go
Java: java
JavaScript and TypeScript: js
Python: py
后面建议接上一个该查询所针对的问题,比如
@id cs/command-line-injection
@id java/string-concatenation-in-loop
*/
控制流写法
/**
* @id problem
* @name problem
* @description problem
* @kind problem
* @problem.severity warning
*/
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking
import UnsafeDeserialization
from RemoteUserInput source, UnsafeDeserializationSink sink
where source.flowsTo(sink)
select source, sink
数字流污点分析写法(返回是与否),终点区别在于@kind 的定义.
**
* @id problem
* @name xiaomingTest
* @description problem
* @kind path-problem
* @problem.severity warning
*/
import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.security.QueryInjection
import DataFlow::PathGraph
import semmle.code.java.security.UnsafeDeserializationQuery
import semmle.code.java.security.XSS
import semmle.code.java.security.JndiInjectionQuery
class MyConfig extends TaintTracking::Configuration {
MyConfig() { this = "不重要的名字" }
override predicate isSource(DataFlow::Node src) {
src instanceof RemoteFlowSource
or exists(MethodAccess call |
call.getMethod().getName()="getCookies" and
src.asExpr()=call
)
}
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess call |
(
call.getMethod().getName()="readObject" or
call.getMethod().getName()="startsWith" or
call.getMethod().getName()="indexOf" or
call.getMethod().getName()="split" or
call.getMethod().getName()="endsWith" or
call.getMethod().getName()="contains"
)
and (
sink.asExpr()=call or
sink.asExpr()=call.getAnArgument()
)
)
}
override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node)
or node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType
or node.getLocation().toString().regexpMatch(".*src/test/.*")
}
}
from MyConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select source.getNode(), source, sink,"source"
类似于白名单机制:isSanitizer是CodeQL的类TaintTracking::Configuration提供的净化方法。它的函数原型是:
override predicate isSanitizer(DataFlow::Node node) {}
在CodeQL自带的默认规则里,对当前节点是否为基础类型做了判断。
override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node)
or node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType
or node.getLocation().toString().regexpMatch(".*src/test/.*")
}
表示如果当前节点是上面提到的基础类型,那么此污染链将被净化阻断,漏洞将不存在。
漏报数据链接:isAdditionalTaintStep方法是CodeQL的类TaintTracking::Configuration提供的的方法,它的原型是:
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {}
//列子
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
isTaintedString(node1.asExpr(), node2.asExpr())
}
它的作用是将一个可控节点A强制传递给另外一个节点B,那么节点B也就成了可控节点。
常用的语法
我们介绍了使用CodeQL表示语法时最常用的标准类:Module、Class、 Function、Stmt以及 Expr类,它们都是AstNode的子类
AstNode
Module -- python模块
Class -- 类
Function -- 函数
Stmt -- 语句
Assert -- assert语句
Assign
AssignStmt -- 赋值语句 x=y
ClassDef -- 类定义语句
FunctionDef -- 函数定义语句
AugAssign -- 自增赋值语句 x+=y
Break -- break语句
Continue -- continue语句
Delete -- del语句
ExceptStmt -- try语句中的except部分
Exec -- exec语句
For -- for语句
If -- if语句
Pass -- pass语句
Print -- print语句
Raise -- raise语句
Return -- return语句
Try -- try语句
While -- while语句
With -- with语句
Expr -- 表达式
Attribute -- 类属性 obj.attr
Call -- 函数调用 f(arg)
IfExp -- 条件表达式 x if cond else y
Lambada -- lambda表达式
Yield -- yield表达式
Bytes -- 字节文字,b"x"或(在python2中)的"x"
Unicode -- Unicode文字,u"x"或(在python3中)的"x"
Num -- 数字文字 3或者4.2这种
IntegerLiteral 整型
FloatLiteral 浮点型
ImaginaryLiteral (这是啥类型)
Dict -- 字典,{'a':2}
Set -- 集合,{'a','b'}
List -- 列表,['a','b']
Tuple -- 元组,('a','b')
DictComp -- 字典推导式,{k:v for ...}
SetComp -- 集合推导式,{x for ...}
ListComp -- 列表推导式,[x for ...]
GenExpr -- 生成器表达式,(x for ...)
Subscript -- 下标操作,seq[index]
Name -- 对变量的引用,var
UnaryExpr -- 一元运算,-x
BinaryExpr -- 二元运算,x+y
Compare -- 比较操作,0<x<10
BoolExpr -- 短路逻辑运算,x and y,x or y
变量 Variables
Variable -- 变量
LocalVariable -- 函数或者类的局部变量
GlobalVariable -- 全局(模块级)变量
其他
Comment -- 注释
参考
https://www.freebuf.com/articles/web/283795.html (java Lombok问题
) 有些问题写的比较好,可借鉴参考