CodeQL 学习笔记【10】调试 CodeQL

使用 CodeQL 进行查询的时候,可能会出现一些意外情况,导致我们查不到自己想要的结果。如果是其他语言,我们可以尝试断点调试、gdb 调试、print 调试等等方法。但是 CodeQL 不一样,它不是一种常规的编程语言,它没法使用常见的那些方法。不过 CodeQL 还是有办法进行调试的。

那就是查看抽象语法树、偏路径图等方法。

但是我们平时写查询的时候进行简单的调试肯定不需要这么复杂的方法。平时我们可以将一个复杂的查询,慢慢简化,简化为一个单独的查询,然后慢慢的去增加子语句,直到发现问题的所在。

或者在一个 predicate 谓词名称上右键,然后选择快速评估。我这里差不多就相当于执行 select inSink

然后你就可以先简单判断是不是谓词写错了?

抽象语法树(AST)

如果说简单测试后发现 sink 和 source 都没有问题,都能正确找到我们想要找的代码,但是整体结果还是查不出来漏洞传播路径。那么问题可能就出现在了 source 到 sink 的流程追踪了。

这个时候就需要 AST 抽象语法树来帮忙了。 我这里还是拿以前写的测试代码来举个例子了。

js 复制代码
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking

module MyFlowConfiguration implements DataFlow::ConfigSig {
  predicate isSource(DataFlow::Node source) {
    source instanceof RemoteFlowSource
  }

  predicate isSink(DataFlow::Node sink) {
    exists(Call call |
      sink.asExpr() = call.getArgument(0) and
      call.getCallee().(Constructor).getDeclaringType().hasQualifiedName("java.net", "URL")
    )
  }
}

module MyFlow = TaintTracking::Global<MyFlowConfiguration>;

from MyFlow::PathNode source, MyFlow::PathNode sink
where MyFlow::flowPath(source, sink)
select source,source.getLocation()

我在刚才在 isSink() 上执行了快速评估,发现 isSink() 确实找到我想要的代码,然后就可以在 java 代码那里鼠标右键选择 CodeQL: View AST

然后就可以在 CodeQL 插件这里看到抽象语法树了。 当你有了抽象语法树,当你鼠标点击一个节点的时候,就会显示一段代码在 CodeQL 中是什么类型。

比如我上面这张图片中高亮的部分的代码 :InputStream in = new URL(url).openStream() 在 CodeQL 中的类型就是 LocalVariableDectStmt

而 new URL(url).openStream() 就是一个 MethodCall。

这样我们就可以很细化的去找我们要找的东西,可以排除一些误报等等之类的。

当然除了查看 AST 还有 getAQlClass() 谓词可以获取一段代码的类型,比如:

js 复制代码
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking

module MyFlowConfiguration implements DataFlow::ConfigSig {
  predicate isSource(DataFlow::Node source) {
    source instanceof RemoteFlowSource
  }

  predicate isSink(DataFlow::Node sink) {
    exists(Call call |
      sink.asExpr() = call.getArgument(0) and
      call.getCallee().(Constructor).getDeclaringType().hasQualifiedName("java.net", "URL")
    )
  }
}

module MyFlow = TaintTracking::Global<MyFlowConfiguration>;

from MyFlow::PathNode source, MyFlow::PathNode sink
where MyFlow::flowPath(source, sink)
select source,source.getAQlClass()

查询的结果就是 PathNode,当然,我这个例子比较傻,毕竟本来 source 定义的时候就是定义的 PathNode ,但你明白意思就行。

部分路径图

还有部分路径图也可以帮助你查看 source 到 sink 的流程从那里断开了,导致查询不到结果。但是用这个东西的时候要特别注意,因为它会将所有 source 到所有 sink 的流向图都展示出来,如果 source 和 sink 都特别多,那么估计电脑会卡死的。

所以我这里演示的时候限制一下代码的文件。

部分路径图有两种形式,一种是正向部分路径图 FlowExplorationFwd (追踪某一个 source 到任意 sink 的路径) ,一种是逆向部分路径图 FlowExplorationRev (追踪一个 sink 到任意 source 的路径).

js 复制代码
/**
 * @name test for partial path graph
 * @description This query tracks data flow from SSRFTask2 source to any sink.
 * @kind path-problem
 * @problem.severity warning
 * @id 5/3
 */

import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking

module MyFlowConfiguration implements DataFlow::ConfigSig {
  predicate isSource(DataFlow::Node source) {
    source instanceof RemoteFlowSource and source.getLocation().getFile().getBaseName() = "SSRFTask2.java"
  }

  predicate isSink(DataFlow::Node sink) {
    exists(Call call |
      sink.asExpr() = call.getArgument(0) and
      call.getCallee().(Constructor).getDeclaringType().hasQualifiedName("java.net", "URL")
    )
  }
}

module MyFlow = TaintTracking::Global<MyFlowConfiguration>;
int explorationLimit() { result = 10 }
module PartialFlow = MyFlow::FlowExplorationFwd<explorationLimit/0>;
import PartialFlow::PartialPathGraph

from PartialFlow::PartialPathNode source, PartialFlow::PartialPathNode sink
where PartialFlow::partialFlow(source, sink, _)
select sink.getNode(), source, sink, "Partial Graph $@.", source.getNode(), "user-provided value."

我这里查到了 5 条路径,哪些 3 步、2 步就结束的路径应该是指向其他 sink 的,但是中途就断了。希望 CodeQL 早日出图形化的展示图吧......

相关推荐
jimmyleeee24 分钟前
大模型安全之三十六:大模型数据管理----从投毒防御到偏见治理的完整框架
人工智能·安全
姜鱼问生1 小时前
三层防火墙排查:Nginx 配好了浏览器还打不开
nginx·安全
终端安全笔记1 小时前
恢复出厂设置清不掉什么:GB 46864 把清除分成三个等级
安全
Summer-Bright3 小时前
深度 | 谷歌Gemini 4 Argon单次输出100万Token:长输出是智能体刚需,先给防御者不给攻击者才是新玩法
人工智能·安全·ai
hasty3 小时前
接口鉴权通过,对象却属于别人:LimeSurvey 跨问卷授权缺陷解析
安全·系统安全
lisw054 小时前
AI如何助力网络安全合规性?
人工智能·安全·可信计算技术
格鸰爱童话5 小时前
搭建primihub开源框架并一步步理解
安全·ai
mennekes5 小时前
组合工业插座箱:场景复杂难配电?插座箱组合方案更安全
安全·制造
林伽一6 小时前
常驻智能体产品化元年开启,安全治理成为产业新门槛|2026年10月1日
人工智能·安全·chatgpt
科力锐品牌君16 小时前
应用级灾备 | 海量非结构化数据如何实现高效数据保护
linux·运维·网络·安全·系统安全·数据安全·灾备