CodeQL

之前上课做过的实验,脚本记录找不到了,只找到实验报告~

本人用的codeql-win64

0 安装

  1. 解析引擎安装
  2. SDK安装
  3. VScode插件安装
  4. 生成并导入database,测试

T1 simpledataflow.py

1.1 源码分析

漏洞代码

程序接受用户输入的数学表达式,检查表达式两项运算数据是否都为整数,通过检查的计算表达式结果并输出,没有通过检查的输出“hacker!”

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 re
class Base:
def __init__(self,expr) -> None:
self.expr = expr

class calc(Base):
def __init__(self, expr) -> None:
super().__init__(expr)
def checkExpr(self):
a = re.split(pattern=r"[+|-|x|/]", string = self.expr)
print(a)
try:
int(a[0])
int(a[1])
except:
return 0
return 1
def getResult(self):
if(self.checkExpr()):
full_expr = "print(%s)"%(self.expr)
print(full_expr)
eval(full_expr) #sink2
return 1
else:
print("hacker!")
return 0

expr = input("expr> ") #source
a = calc(expr)
a.getResult()

# 单模块继承类 作为 baseline

计算结果时,程序使用了危险函数eval()

image-20260109103658527

人为朴素分析数据流:expr 是通过 input 函数获取的用户输入,并通过 getResult() 方法传递给 eval() 函数执行,这里 eval()存在任意代码执行漏洞。

通过分析,将source定义为input()返回值,sink定义为eval()参数

1.2 ql查询

1.为目标代码生成数据库

1
2
cd ./target
codeql database create ./codeql-test1 --language=python

并在vscode DATABASE中选择目标文件夹codeql-test1

2.编写查询代码

  • 原项目在\yourpath\CodeQL\ql\python\ql\src\Security\CVE-..中提供了许多ql查询示例,但仅供参考。

  • github文档中也提供了示例参考Analyzing data flow in Python — CodeQL(这个doc更靠谱)

但本人代码拼拼凑凑,一直报错,最后求助大佬同桌~

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 python
import semmle.python.dataflow.new.TaintTracking
import semmle.python.dataflow.new.DataFlow
import semmle.python.ApiGraphs

//定义source和sink
module TestTaintTrackingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(CallNode call |
call.getFunction().(NameNode).getId() = "input" and
source.(DataFlow::CfgNode).getNode() = call.getASuccessor()
)
}

predicate isSink(DataFlow::Node sink) {
exists(CallNode call |
call.getFunction().(NameNode).getId() = "eval" and
sink.(DataFlow::CfgNode).getNode() = call.getAnArg()
)
}
}

//路径查询
module TestTaintTrackingFlow = TaintTracking::Global<TestTaintTrackingConfig>;

from TestTaintTrackingFlow::PathNode source, TestTaintTrackingFlow::PathNode sink
where TestTaintTrackingFlow::flowPath(source, sink)
select source, sink

3.右击ql程序,点击Run against local database

查询结果,路径连通,与预期一致

PS:同桌的代码可以跑出两个路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import python
import semmle.python.dataflow.new.TaintTracking
import semmle.python.ApiGraphs

module InputToEvalConfiguration implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source = API::builtin("input").getACall()
}

predicate isSink(DataFlow::Node sink) {
exists(DataFlow::CallCfgNode call |
call = API::builtin("eval").getACall() and
sink = call.getArg(0)
)
}
}

module InputToEvalFlow = TaintTracking::Global<InputToEvalConfiguration>;
import InputToEvalFlow::PathGraph

from InputToEvalFlow::PathNode input, InputToEvalFlow::PathNode eval
where InputToEvalFlow::flowPath(input, eval)
select input, eval

image-20260109121235193

T2 muchmorecomplicated

2.1 程序分析

有四个Go语言文件,是Grafana的一部分,构成了Grafana的插件管理和API服务器的核心逻辑。

  • manager.go 插件管理器
  • plugins.go 处理插件相关API请求
  • api.go Grafana API的路由和相关的处理函数
  • http_server.go 定义HTTP服务器的结构和行为

上网搜索发现,可能存在任意文件读取漏洞 CVE-2021-43798

image-20260109152451540

  • 漏洞存在于plugins.go中,当输入的 pluginId存在时,会匹配*内容,使用filepath.Clean清理路径中的多余字符后,直接拼接到pluginFilePath,然后使用os.open(pluginFilePath)打开该文件,最终回显到页面。
  • filepath.Clean()尽管进行了处理,但仍然可以利用。
  • 并且plugins api权限为public,是未授权的,任何人都可以查看。

2.2 ql查询

1.生成数据库记得生成language=go

2.查询代码

  • Sink定义为os.open()
  • Source开始设置为api.go中的get(),但无法联通,后来将 source 定为 Params 函数, 可以成功连通

image-20260109160045440

之前有师傅通过添加isAdditionalTaintStep 连接数据流

参考这位师傅的分析过程

safe6Sec/codeql-grafana: 用codeql分析grafana最新任意文件读取

总代码

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
import go
import semmle.go.security.TaintedPathCustomizations::TaintedPath
import codeql.dataflow.DataFlow

class Source extends DataFlow::Node{
Source(){
exists( DataFlow::CallExpr call |
call.getCalleeName().toString() = "Params"|
call =this.asExpr()
)
}
}

class Sink extends DataFlow::Node{
Sink(){
exists( Function f,CallExpr call |
f.hasQualifiedName("os", "Open") and
call.getTarget() = f and call.getAnArgument()=this.asExpr()
)
}
}

class Configuration extends TaintTracking::Configuration{
Configuration(){ this = "xxx"}
override predicate isSource(DataFlow::Node source) {
source instanceof Source
}
override predicate isSink(DataFlow::Node sink) {
sink instanceof Sink
}
}

from Configuration cfg, DataFlow::PathNode sink, DataFlow::PathNode source
where cfg.hasFlowPath(source, sink)
select source,sink