CodeQL 的学习以及尝试漏洞挖掘
2020-09-11 01:00:00 Author: bestwing.me(查看原文) 阅读量:321 收藏

CodeQL 背景

CodeQL 是一个白盒源代码审计工具。其开发公司 Semmle 也成功和 Github 联姻,成立了 Github Security Lab,负责 Github 上开源软件的代码安全审计。

网上关于该工具的安装教程见官方文档,顺便一说网上的教程也不少,这里就不赘述了。

CodeQL 使用

官方提供了 QL 语法的文档: https://help.semmle.com/QL/ql-handbook/ 以及 CodeQL 的一些 api 接口 https://help.semmle.com/qldoc/cpp/

关于CodeQL 使用,在这篇文章咱们以一个 Github Security Lab 公开的教程作为示例

CodeQL CTF 2: U-Boot

  • CTF 2: U-Boot Challenge - Follow in the footsteps of our security research team and discover 13 vulnerabilities un U-Boot. Language: C - Difficulty level:

0x01 查找特定函数的定义

CodeQL 使用的时候需要通过 import 关键词导入特定语言的解析库,例如这里使用的是 import cpp ,如果我要查询 strlen 的函数定义,我只需要编写如下代码

1
2
3
4
5
6
import cpp

from Function f
where f.getName() = "strlen"
select f, "a function named strlen"

右键点击运行,效果如下

其中第三个是关于 strlen 的定义,

0x2 查找特定宏定义

但是在 c 代码里, 有些情况我们需要查找宏的定义,这个时候就需要使用 Macro 这个 Predicates, 例如样例里提到的 ntohs 族,函数

另外, QL 语言支持正则匹配,我们可以通过 regexpMatch 匹配一类函数例如如下代码

1
2
3
4
5
6
import cpp

from Macro m
where m.getName().regexpMatch("ntoh(s|l|ll)")
select m

0x3 函数的调用

在代码审计的场景里,我们在查找函数定义的同时,也需要根据函数调用查找完整的数据流,在 CodeQL 里,函数的调用通过 FunctionCall 这个 Predicates 可以直接完成,例如如下代码

1
2
3
from FunctionCall c
where c.getTarget().getName() = "memcpy"
select c

0x04 宏定义的调用

查找宏定义的的调用,使用 MacroInvocation 完成,代码如下

1
2
3
from MacroInvocation mi
where mi.getMacro().getName().regexpMatch("ntoh(s|l|ll)")
select mi

0x05 获取 ntohs 族宏定义的表达式

在 0x04 中,我们提到了宏定义的调用,另外我们知道, ntoh 族函数,通常用来进行网络字节序到主机字节序的转换,通常而言,如果是一个网络协议,我们可能会从某个字段中取出某个特定的值,并且赋值给某个变量,这个时候我们就需要获取他们的表达式。

这里以表达式出现的话,我们可以使用 getExpr()函数完成,仅仅只需要将 select mi 修改为 select mi.getExpr() ,效果如下:

1
2
3
4
5
6
import cpp

from MacroInvocation mi
where mi.getMacro().getName().regexpMatch("ntoh(s|l|ll)")
select mi.getExpr()

例如这里的赋值语句就是第 78 个表达式

0x06 编写一个 QL 类

QL 类包括三个部分

  1. 关键字class
  2. 类的名称。这是一个 以大写字母开头的标识符
  3. 要扩展的类型。
  4. 主体,用大括号括起来。

更多关于类的编写可以参考 https://help.semmle.com/QL/ql-handbook/types.html#classes

这里我们需要编写尝试编写一个 NetworkByteSwap 的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import cpp

/**
* An expression involved when swapping the byte order of network data.
* Its value is likely to have been read from the network.
*/
class NetworkByteSwap extends Expr {
NetworkByteSwap() {
exists(MacroInvocation mi |
mi.getMacroName().regexpMatch("ntoh(s|l|ll)") and
this = mi.getExpr()
)
}
}

from NetworkByteSwap n
select n

0x07 数据流分析

现在我们来开始做数据流分析,通过定义源和接收器来创建配置类。 来源应该是调用ntohlntohllntohs。该接收器应为不安全调用memcpy的size参数。通过查找此类的数据流判断是否存在安全问题

这里需要使用

1
2
import semmle.code.cpp.dataflow.TaintTracking
import DataFlow::PathGraph

两个新库,然后我们要设置 来源和 Sink 的对象。

首先设置来源:

1
override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof NetworkByteSwap }

设置来源对象为 表达式,是 NetworkByteSwap 这个类的值,NetworkByteSwap 这个类在 0x06 定义

然后设置接收器,接收器为 memcpy 的size 参数

1
2
3
override predicate isSink(DataFlow::Node sink) {
exists(FunctionCall c | c.getTarget().getName() = "memcpy" and sink.asExpr() = c.getArgument(2))
}

完整代码如下:

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
import cpp
import semmle.code.cpp.dataflow.TaintTracking
import DataFlow::PathGraph

/**
* An expression involved when swapping the byte order of network data.
* Its value is likely to have been read from the network.
*/
class NetworkByteSwap extends Expr {
NetworkByteSwap() {
exists(MacroInvocation mi |
mi.getMacroName().regexpMatch("ntoh(s|l|ll)") and
this = mi.getExpr()
)
}
}

class Config extends TaintTracking::Configuration {
Config() { this = "Config: this name doesn't matter" }

override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof NetworkByteSwap }

override predicate isSink(DataFlow::Node sink) {
exists(FunctionCall c | c.getTarget().getName() = "memcpy" and sink.asExpr() = c.getArgument(2))
}
}

from Config cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
select sink, source, sink, "Network byte swap flows to memcpy"

效果如下

其中有多个明显的缓冲区溢出漏洞

使用CodeQL 进行 0day 挖掘

我们通过 CodeQL 编写了一个 ntoh -> memcpy 的数据流追踪,当我们通过检查相应的代码我们即可分析是否有缓冲区溢出风险。我们学习了差不多了,我们可以通过这个成型的查询来查询一些类似这样的安全问题。

例如我通过这个查询,挖掘了两个缓冲区溢出漏洞。

大家也可以去尝试尝试。


文章来源: https://bestwing.me/codeql.html
如有侵权请联系:admin#unsafe.sh