3 CodeQL U-Boot Challenge
在 Github Learning Lab 中,有一个用于学习 CodeQL 的入门课程 - CodeQL U-Boot Challenge (C/C++)]( https://lab.github.com/GitHubtraining/codeql-u-boot-challenge-(cc++) )
- 编写一个简单的查询,用于查询
strlen
函数的定义位置。
import cpp
from Function f
where f.getName() = "strlen"
select f, "a function named strlen"
- 分析这个简单的查询,之后查询一下
memcpy
函数
import cpp
from Function f
where f.getName() = "memcpy"
select f, "a function named memcpy"
- 使用不同的类以及不同的谓词。这里我们编写 QL 查找名为
ntohs
、ntohl
以及ntohll
的宏定义。
import cpp
from Macro macro
# where macro.getName() = "ntohs" or macro.getName() = "ntohl" or macro.getName() = "ntohll"
where macro.getName().regexpMatch("ntoh(s|l|ll)")
select macro
- 使用双变量。通过使用多个变量来描述复杂的代码关系,查询特定函数的调用位置。
import cpp
from FunctionCall c, Function f
where c.getTarget() = f and f.getName() == "memcpy"
select c
- 使用 Step6 的技巧,查询宏定义的调用位置。
import cpp
from MacroInvocation mi
where mi.getMacro().getName().regexpMatch("ntoh(s|l|ll)")
select mi
- 改变 select 的输出。查找这些宏调用所扩展到的顶级表达式(宏展开)。
import cpp
from MacroInvocation mi
where mi.getMacro().getName().regexpMatch("ntoh(s|l|ll)")
select mi.getExpr() # 注意这里的.getExpr()
- 实现一个类。用
exists
关键字来引入一个临时变量,以设置当前类的数据集合;特征谓词在声明时会被调用以确定当前类的范围,类似于 C++ 构造函数。
特征谓词在声明时会被调用以确定当前类的范围,类似于 C++ 构造函数。查询语句中的类中,先通过 exists
量词创建一个临时变量 mi
来表示被调用的宏的名字,如果被调用的的宏展开后和当前代码片段相等,则这个表达式属于这个集合。
import cpp
class NetworkByteSwap extends Expr {
NetworkByteSwap() {
exists(MacroInvocation mi |
mi.getMacroName().regexpMatch("ntoh(s|l|ll)") and
this = mi.getExpr()
)
}
}
from NetworkByteSwap n
select n, "Network byte swap"
- 污点追踪
借助前面几步,基本描述了 CodeQL 的使用。最后一个测试是使用 CodeQL 进行污点追踪。这里使用了 CodeQL 的全局污点追踪 (Global taint tracking)。新定义的 Config
类继承于 TaintTracking::Configuration
。类中重载的 isSource
谓语定义为污点的源头,而 isSink
定义为污点汇聚点。
有时候,远程输入的数据可能经过 ntoh
函数处理,通过转换字节序得到相应的数字。而 memcpy
的第 2 个参数如果控制不当,可造成数据溢出。将上面两个结论结合起来,如果有一个远程输入的数据通过字节序变换得到的数字,在未经过校验的情况下,作为了 memcpy
的第二个参数,那么就有可能造成数据溢出。
在 isSource
中,判断 source
的 Expr
是否是 NetworkByteSwap
这个类,来判断污点的源头。
在 isSink
中,我们使用了辅助类 FunctionCall
判断函数调用是否为 memcpy
且 sink
的代码片段是否为 memcpy
的第二个参数;最后一句则是判断函数的第一个参数是否为常量,如果为常量的话基本不可能出现问题,所有忽略。
import cpp
import semmle.code.cpp.dataflow.TaintTracking
import DataFlow::PathGraph
# 设置用于交换网络数据的类
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 = "NetworkToMemFuncLength" }
override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof NetworkByteSwap }
override predicate isSink(DataFlow::Node sink) {
exists(FunctionCall call |
call.getTarget().getName() = "memcpy" and
sink.asExpr() = call.getArgument(2) and
not call.getArgument(1).isConstant()
)
}
}
# 查询
from Config cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
select sink, source, sink, "Network byte swap flows to memcpy"
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论