当前位置:   article > 正文

代码审计-Codeql-1_代码审计 codesqli

代码审计 codesqli

上篇讲解啦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 覆盖之前生成的数据库,如果不加上该命令,若存在同名数据库,则报错。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

非编译型语言

# python项目
codeql database create --language=python <output-folder>/python-database
# 指定优先提取器创建
codeql database create --language=go testdb --search-path ../build/codeql-extractor-go
  • 1
  • 2
  • 3
  • 4

编译型语言

# 使用以下内容构建的 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'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

执行分析

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 (必填) 使用的查询语句
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

测试

# 测试单个文件:
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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

基础语法

非污点跟踪

MethodAccess :调用的方法表达式
Method : class中的方法
getCaller :方法调用所在的方法,换句话说是哪个方法体中调用的
getCallee: 被调用的方法
hasName(param):限定名称
getDeclaringType():获取此变量的类型
hasQualifiedName("package","class"):限定类名
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

样例:

/**
 * @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
*/
  • 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

控制流写法

/**
 * @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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

数字流污点分析写法(返回是与否),终点区别在于@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"
  • 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

类似于白名单机制:isSanitizer是CodeQL的类TaintTracking::Configuration提供的净化方法。它的函数原型是:

override predicate isSanitizer(DataFlow::Node node) {}
  • 1

在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/.*")
  }
  • 1
  • 2
  • 3
  • 4
  • 5

表示如果当前节点是上面提到的基础类型,那么此污染链将被净化阻断,漏洞将不存在。
漏报数据链接: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())
}
  • 1
  • 2
  • 3
  • 4
  • 5

它的作用是将一个可控节点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 -- 注释
  • 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

参考

https://www.freebuf.com/articles/web/283795.html (java Lombok问题
) 有些问题写的比较好,可借鉴参考

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/514685
推荐阅读
相关标签
  

闽ICP备14008679号