安装
CodeQL CLI引擎安装,https://github.com/github/codeql-cli-binaries/releases
1 2 3 4 5 6 wget https://github.com/github/codeql-cli-binaries/releases/download/codeql-osx64.zip echo "alias codeql=\"/Users/alphag0/Desktop/CodeQL/codeql/codeql\"" >> ~/.zshrcsource ~/.zshrc
CodeQL SDK安装,https://github.com/github/codeql
VSCode CodeQL扩展安装,扩展中搜索CodeQL进行安装,并配置CLI地址
数据库
准备源代码,对于闭源代码可以通过反编译的方式来获取(例如Java项目),本文基于项目micro_service_seclab 进行学习
利用CodeQL CLI创建数据库
1 2 3 4 5 codeql database create <数据库名> --language=<语言标识符> --source-root=<源码路径> codeql database create ./databases/micro_service_seclab --language="java" --command ="mvn clean compile -DskipTests" --source-root=./micro_service_seclab/ --overwrite
创建完成后在VSCode插件中加载编译后的数据库,选择Language为Java,安装依赖
基本用法 CodeQL包含用于查找每种受支持语言中最相关、最有趣的问题的查询功能,还可以编写自定义查询来查找与项目相关的特定问题,重要的查询类型包括:
警报查询,用于突出显示代码中特定位置问题的查询
路径查询,描述代码中source和sink之间信息流的查询
CodeQL报警查询基本结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 import from where select
CodeQL路径查询基本结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import < language > / / For some languages (Java/ C+ + / Python/ Rust/ Swift) you need to explicitly import the data flow library, such as / / import semmle.code.java.dataflow.DataFlow or import codeql.swift.dataflow.DataFlow... module Flow = DataFlow::Global < MyConfiguration> ;import Flow::PathGraph from Flow::PathNode source, Flow::PathNode sinkwhere Flow::flowPath(source, sink)select sink.getNode(), source, sink, "<message>"
CodeQL查询文件(.ql)元数据属性:
属性
值类型
描述
@description
<text>
用一句话或一段话描述查询的目的以及结果的_用途_或重要性
@id
<text>
由小写字母或数字组成的单词序列,以逗号/或空格分隔-,用于标识和分类查询
@previous-id
<text>
表示该查询结果之前已在其他查询中报告过
@kind
problempath-problem
标识查询是警报查询还是路径查询
@name
<text>
用于定义查询标签的语句
@tags
correctnessmaintainabilityreadabilitysecurity
这些标签将查询归类到不同的类别中,以便于搜索和识别
@precision
lowmediumhighvery-high
表示查询结果中真正阳性结果(而非假阳性结果)的百分比
@problem.severity
errorwarningrecommendation
定义非安全查询生成的任何警报的严重级别
@security-severity
<score>
定义查询的严重级别,介于0.0和10.0之间
有关CodeQL中包含的谓词、模块和类的详细信息,https://codeql.github.com/codeql-standard-libraries/
在CodeQL中存在两种数据流:
本地数据流:本地数据流是指单个方法或可调用函数内的数据流,本地数据流通常比全局数据流更简单、更快速、更精确,并且足以应对许多查询
全局数据流:全局数据流跟踪整个程序的数据流,然而,全局数据流的精确度低于局部数据流,并且分析通常需要更多的时间和内存
针对项目的部分漏洞学习全局数据流,查询语句如下:
XXE漏洞查询语句
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 import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.TaintTracking import semmle.code.java.dataflow.FlowSources module XXEConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource } predicate isSink(DataFlow::Node snk) { exists (MethodCall call | call.getMethod().hasName("parse") and snk.asExpr() = call.getArgument(0 ) ) } } module XXEFlow = TaintTracking::Global < XXEConfig> ;import XXEFlow::PathGraph from XXEFlow::PathNode src, XXEFlow::PathNode snkwhere XXEFlow::flowPath(src, snk)select src.getNode(), src, snk, "远程输入数据可能流入 parse 方法, 存在 XXE 等风险"
Fastjson漏洞查询语句
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 import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.TaintTracking import semmle.code.java.dataflow.FlowSources module FastjsonConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource } predicate isSink(DataFlow::Node snk) { exists (MethodCall call | call.getMethod().hasName("parseObject") and snk.asExpr() = call.getArgument(0 ) ) } } module FastjsonFlow = TaintTracking::Global < FastjsonConfig> ;import FastjsonFlow::PathGraph from FastjsonFlow::PathNode src, FastjsonFlow::PathNode snkwhere FastjsonFlow::flowPath(src, snk)select src.getNode(), src, snk, "远程输入数据可能流入 parseObject 方法, 存在 Fastjson 反序列化等风险"
SQL漏洞查询语句
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 import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.TaintTracking import semmle.code.java.dataflow.FlowSources module SQLConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource } predicate isSink(DataFlow::Node snk) { exists (MethodCall call | call.getMethod().hasName("query") and snk.asExpr() = call.getArgument(0 ) ) } predicate isBarrier(DataFlow::Node sanitizer) { sanitizer.getType() instanceof NumberType or exists (ParameterizedType pt | sanitizer.getType() = pt and pt.getTypeArgument(0 ) instanceof NumberType ) } } module SQLFlow = TaintTracking::Global < SQLConfig> ;import SQLFlow::PathGraph from SQLFlow::PathNode src, SQLFlow::PathNode snkwhere SQLFlow::flowPath(src, snk)select src.getNode(), src, snk, "远程输入数据可能流入 query 方法, 存在 SQL 注入等风险"
RCE漏洞查询语句
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 import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.TaintTracking import semmle.code.java.dataflow.FlowSources module RCEConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource } predicate isSink(DataFlow::Node snk) { snk.asExpr() instanceof ArgumentToExec } } module RCEFlow = TaintTracking::Global < RCEConfig> ;import RCEFlow::PathGraph from RCEFlow::PathNode src, RCEFlow::PathNode snkwhere RCEFlow::flowPath(src, snk)select src.getNode(), src, snk, "远程输入数据可能流入 exec 方法, 存在 RCE 风险"
语法片段
获取setter方法
1 2 3 4 5 6 7 8 class SetterMethod extends Method { SetterMethod() { this.getReturnType() instanceof VoidType and this.getNumberOfParameters() = 1 and this.getName().matches ("set%") } }
获取getter方法
1 2 3 4 5 6 7 8 9 10 11 class GetterMethod extends Method { GetterMethod() { this.getNumberOfParameters() = 0 and not this.getReturnType() instanceof VoidType and ( this.getName().matches ("get%") or this.getName().matches ("is%") ) } }
获取无参构造方法
1 2 3 4 5 6 class NoArgConstructor extends Constructor { NoArgConstructor() { this.getNumberOfParameters() = 0 } }
获取toString/hashCode/equals方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class ToStringMethod extends Method { ToStringMethod() { this.hasName("toString") and this.getNumberOfParameters() = 0 and this.getReturnType().hasQualifiedName("java.lang", "String") } } class HashCodeMethod extends Method { HashCodeMethod() { this.hasName("hashCode") and this.getNumberOfParameters() = 0 and this.getReturnType().hasQualifiedName("int") } } class EqualsMethod extends Method { EqualsMethod() { this.hasName("equals") and this.getNumberOfParameters() = 1 and this.getParameter(0 ).getType() instanceof TypeObject and this.getReturnType().hasQualifiedName("boolean") } }
参考 CodeQL Documentation
CodeQL library for Java/Kotlin