codeql_zero_to_hero 学习

codeql_zero_to_hero 学习

henry Lv4

https://github.com/GitHubSecurityLab/codeql-zero-to-hero

Challenge 1

Enable code scanning with CodeQL on a public repository

方法一 直接fork出来一个仓库,然后直接用codeql扫

按下列步骤操作即可

Snipaste_2024-12-03_22-22-37

default

Snipaste_2024-12-03_22-22-54

进入到下面的页面,然后Enable CodeQL

Snipaste_2024-12-03_22-23-17

跳转到新的界面

Snipaste_2024-12-03_22-23-39

往下滑就会看到这里,表示已经开始扫了,一般过几分钟就会出结果

Snipaste_2024-12-03_22-23-51

Snipaste_2024-12-03_23-16-17

方法二 fork 其他的仓库,并使用codeql进行扫描

这里我们以imagemagic为例

Snipaste_2024-12-03_23-36-41

这里需要注意的一个点是删除上图.github中的workflows目录,原因如下:

GitHub Actions(自动化测试工具)会在代码库中定义工作流(workflows),如自动化测试、构建、部署等。工作流通常定义在 .github/workflows/ 目录中,以 .yml文件形式存在。

fork 一个开源项目的代码仓库时,其中的 Actions 工作流默认是被 禁用 的。这是出于 安全考虑,主要有以下原因:

​ 1. 防止恶意代码执行

​ • 原仓库的工作流可能包含危险命令(如删除文件、窃取密钥等),fork 后自动运行可能带来安全隐患。

​ 2. 防止意外消耗资源

​ • GitHub Actions 有免费使用限制(如 Actions minutes),自动运行可能消耗不必要的时间配额。

删除后 Actions 中开启 workflow

Snipaste_2024-12-03_23-47-20

然后进入Security重复方法一中的操作。

Snipaste_2024-12-03_23-48-35

References:

https://github.blog/developer-skills/github/codeql-zero-to-hero-part-2-getting-started-with-codeql/

Challenge 2

Set up VS Code CodeQL starter workspace

在vscode的扩展中安装codeql,安装完之后打开终端,创建一个测试文件夹用来使用,之后命令如下:

1
2
3
4
$ git clone https://github.com/github/vscode-codeql-starter.git
$ cd vscode-codeql-starter
$ git submodule init
$ git submodule update --recursive

然后在 VS Code 中依次点击 File -> Open Workspace from File… vscode-codeql-starter.code-workspace,界面如下:

Snipaste_2024-12-10_11-30-00

后面进入到如下界面然后点击圆框中的图标,输入GitHubSecurityLab/codeql-zero-to-hero回车。

Snipaste_2024-12-10_11-30-57

回到 workspace folder中的 codeql-custom-queries-python文件夹下,创建一个新的文件 test.ql,添加下面的内容 select "Hello World!",点击运行就会出现我们所需的结果。

Snipaste_2024-12-10_11-32-37

Challenge 3

Create CodeQL database using CodeQL CLI locally

1
2
3
4
5
6
7
gh extensions install github/gh-codeql
gh codeql install-stub

#Create a CodeQL database
git clone https://github.com/GitHubSecurityLab/codeql-zero-to-hero.git
cd codeql-zero-to-hero
gh codeql database create example-codeql-db --language=python

进入到 VS Code CodeQL 扩展, 点击 “Choose Database from folder” 然后选中刚刚创建的 “example-codeql-db”

CodeQL基本用法

CodeQL 查询的基本语法和结构类似于 SQL 语法,由三个语句组成from——、whereselect,描述了我们要查找的内容。

  • from定义要查询的类型和变量。
  • where以逻辑公式的形式定义这些变量的条件。where如果没有条件,可以省略。
  • select定义查询的输出。

Challenge 4

选定 challenge 3 中创建的数据库,然后回到vscode工作目录,在codeql-custom-queries-python中创建call.ql,输入以下内容:

1
2
3
4
5
import python

from Call call
where call.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*")
select call

1. import python

这一行表明查询要分析的是 Python 源代码,并导入了与 Python 分析相关的 CodeQL 模块。

CodeQL 支持多种语言(如 Java、C++、JavaScript 等),通过 import 来选择目标语言模块。

2. from Call call

Call 是 CodeQL 中的一个类,用于表示代码中的函数调用点。

call 是一个变量名,它代表查询时匹配到的函数调用点实例。

from Call call 的作用是从 CodeQL 数据库中选出所有属于 Call 类的函数调用点。

3. where call.getLocation().getFile().getRelativePath().regexpMatch(“2/challenge-1/.*“)

这一行定义了查询的条件,逐步解释如下:

call.getLocation():获取函数调用点所在的位置(location),例如代码的行号、文件等。

getFile():从位置中获取调用点所在的文件。

getRelativePath(): 获取文件的相对路径(相对于项目根目录)。

regexpMatch("2/challenge-1/.*"): 使用正则表达式匹配文件路径。

最终结果如下:

Snipaste_2024-12-10_15-49-40

精确 QL 查询

1
2
3
4
5
6
7
import python

from Call c, Name name
where name.getId() = "eval" and
c.getFunc() = name and
c.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*")
select c

其中 Name 表示指向代码中的所有变量名,Call表示指向所有方法调用,这里分别用 c,name表示,同时 QL 支持三种逻辑操作 andornot

这里主要引入了内容name.getId() = "eval" and c.getFunc() = name,表示这里只关心 eval 这个方法调用。

Challenge 5

将上面的代码跑一遍,找到eval方法调用的位置

Snipaste_2024-12-10_16-17-24

Challenge 6

1
2
3
4
5
import python

from Function f
where f.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*")
select f, "This is a function"

这个主要是用来实现搜索特定函数,比如在python中我们以def func定义的函数,注意要与Call进行区分。

Snipaste_2024-12-10_16-17-11

Predicates

1
2
predicate <name>(<variable type>:<variable name>) {
}

Challenge 7

1
2
3
4
5
6
7
8
9
10
11
import python

predicate isEvalCall(Call c, Name name) {
name.getId() = "eval" and
c.getFunc() = name
}

from Call c, Name name
where isEvalCall(c, name) and
c.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*")
select c, "call to eval"

这里定义了一个谓词 predicates 逻辑,其实就是将先前的两个判断逻辑加入到了里面,并进行了一个简单的封装并方便我们进行重用,结果依然与先前的结果一致。

Class

1
2
3
4
class <name> extends <type> {
<characteristic predicate>() {
}
}

每一个扩展类都应该配备一个父类,在下面的例子中就是 Call,其中 this 表示的就是 Call 实例。

由于这里引入类之后,name变量不在存在,所以这里需要exists() 进行构造,它允许我们定义局部变量。首先定义局部变量,然后用竖线 将它们与条件分开|。接下来的所有条件都可以用|and,形式如 exists( | | ),比如 exists(Name name | this.getFunc() = name | name.getId() = "eval")

Challenge 8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import python

class EvalCall extends Call {
EvalCall() {
exists(Name name |
this.getFunc() = name |
name.getId() = "Flask")
}
}

from Call c
where c instanceof EvalCall and
c.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*")
select c, "call to 'Flask'."

Snipaste_2024-12-10_16-49-58

Challenge 9

简单把前面的知识过了一遍

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
import python


predicate isCommandFunc(Function f) {
f.getName().regexpMatch(".*command.*")
}

class CommandFunc extends Function {
CommandFunc() {
this.getName().regexpMatch(".*command.*")
}
}

//method 1
// from Function f
// where f.getName().regexpMatch(".*command.*") and
// f.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*")
// select f

//method 2
// from Function f
// where isCommandFunc(f) and
// f.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*")
// select f

//method 3
from Function f
where f instanceof CommandFunc and
f.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*")
select f, "find command func"

Snipaste_2024-12-10_17-14-29

Challenge 10

Query the AST

这里一开始把我搞懵了,因为我没有找到所谓的 source archive,后面通过一通探索,操作如下:

首先找到我们建立的database,然后右键Add Database Source...

Snipaste_2024-12-10_17-46-24

这时候我们就可以在工作目录中找到source archive,然后选一个想查看AST的源码文件,右键点击Code QL: View AST即可。

Snipaste_2024-12-10_17-48-16

Challenge 11

需要学习codeql文档:https://codeql.github.com/docs/writing-codeql-queries/ql-tutorials/

一些聚合词学习

例子 结果
`min(Person p p.getLocation() = “east”
`count(Person p p.getLocation() = “south”
`avg(Person p
`sum(Person p p.getHairColor() = “brown”

这里 override 可以用来覆盖原Person中的isAllowedIn方法

1
2
3
4
5
6
7
8
9
class Child extends Person {
/* the characteristic predicate */
Child() { this.getAge() < 10 }

/* a member predicate */
override predicate isAllowedIn(string region) {
region = this.getLocation()
}
}
Predicate Description
parentOf(Person p) returns a parent of p

我们知道国王自己没有孩子,但也许他有兄弟姐妹。写一个查询来找出答案:

1
2
3
4
from Person p
where parentOf(p) = parentOf("King Basil") and
not p = "King Basil"
select p

他确实有兄弟姐妹!但你需要检查他们中是否有人还活着……以下是你可能还需要的一个谓词:

Predicate Description
isDeceased() holds if the person is deceased

使用这个谓词来查看国王的兄弟姐妹是否还活着。

1
2
3
4
5
from Person p
where parentOf(p) = parentOf("King Basil") and
not p = "King Basil"
and not p.isDeceased()
select p

不幸的是,巴西尔国王的兄弟姐妹都不在世了。

Challenge 12

随便找一个 security queries 运行就可以了python/ql/src/Security/CWE-089/SqlInjection.ql

  • Title: codeql_zero_to_hero 学习
  • Author: henry
  • Created at : 2024-12-11 14:09:13
  • Updated at : 2024-12-11 14:20:42
  • Link: https://henrymartin262.github.io/2024/12/11/codeql_note/
  • License: This work is licensed under CC BY-NC-SA 4.0.
 Comments