c - Semmle QL : TaintTracking hasFlow() Problem with Sources that taint their Arguments

标签 c semmle-ql

我想对使用用户输入污染其参数的函数进行 TaintTracking。示例:

fgets(buf, sizeof(buf), stdin); // buf is tainted
[...]
n = strlen(buf); // tainted argument to strlen
[...]
memcpy(somewhere, buf, n) // tainted call to memcpy

Semmle 应该能够通过如下所示的查询发现这一点(仅以 fgets->strlen 为例)。我从 SecurityOptions 借用代码:

import cpp
import semmle.code.cpp.dataflow.TaintTracking

class IsTaintedArg extends string {
  IsTaintedArg() { this = "IsTaintedArg" }
  predicate userInputArgument(FunctionCall functionCall, int arg) {
    exists(string fname |
      functionCall.getTarget().hasGlobalName(fname) and
      exists(functionCall.getArgument(arg)) and (fname = "fgets" and arg = 0) // argument 0 of fgets is tainted
    )
  }

  predicate isUserInput(Expr expr, string cause) {
    exists(FunctionCall fc, int i |
      this.userInputArgument(fc, i) and
      expr = fc.getArgument(i) and
      cause = fc.getTarget().getName()
    )
  }
}

class TaintedFormatConfig extends TaintTracking::Configuration {
  TaintedFormatConfig() { this = "TaintedFormatConfig" }
  override predicate isSource(DataFlow::Node source) {
    exists (IsTaintedArg opts |
      opts.isUserInput(source.asExpr(), _)
    )
  }
  override predicate isSink(DataFlow::Node sink) { 
    exists (FunctionCall fc | sink.asExpr() = fc.getArgument(0) and fc.getTarget().hasName("strlen")) // give me all calls that land in strlen's first argument
  }
}

from TaintedFormatConfig cfg, DataFlow::Node source, DataFlow::Node sink
where cfg.hasFlow(source, sink)
select sink, source

但它看起来并不正常。

但是,当我只查询 cfg.isSource()cfg.isSink() 时,source 和 sink 都会被识别。但是 hasFlow() 仍然没有返回任何内容 - 尽管路径肯定存在。

  • 我正在使用 libssh2 来测试我的发现,示例流程存在 here .

  • 我要测试的查询是 here .

有人知道我在上面的查询中可能做错了什么吗?

最佳答案

缺少的位在 isSource 中,其中污点跟踪从 fgets 的第 0 个参数开始。使用 asExpr 将描述从该参数 fgets 的流程。我们想要的是通过该参数的流出 fgets。您可以通过将 asExpr 替换为 asDefiningArgument 来获得它。

Here是指向您的查询结果的链接,我在 isSource 中同时使用了 asExprasDefiningArgument。这意味着如果您将来扩展 isUserInput,它的表达式将被视为来源,既作为它们的值,也作为输出参数。

新版查询有8个结果,有些只看source和sink很难理解,因为它们可以在不同的文件中。我制作了一个清理版本的查询

  • 生成源和汇之间的路径解释(@kind path-problem),
  • 删除辅助谓词的 IsTaintedArg 类包装,
  • 删除一些未使用的参数和检查,以及
  • 将调用添加到 asDefiningArgument

这是完整的查询:

/**
 * @kind path-problem
 * @id taint-to-strlen
 */
import cpp
import semmle.code.cpp.dataflow.DataFlow
import DataFlow::PathGraph

predicate userInputArgument(FunctionCall functionCall, int arg) {
  functionCall.getTarget().hasGlobalName("fgets") and
  arg = 0 // argument 0 of fgets is tainted
}

predicate isUserInput(Expr expr) {
  exists(FunctionCall fc, int i |
    userInputArgument(fc, i) and
    expr = fc.getArgument(i)
  )
}

class TaintedFormatConfig extends DataFlow::Configuration {
  TaintedFormatConfig() { this = "TaintedFormatConfig" }
  override predicate isSource(DataFlow::Node source) {
    isUserInput(source.asExpr())
    or
    isUserInput(source.asDefiningArgument())
  }
  override predicate isSink(DataFlow::Node sink) { 
    exists (FunctionCall fc | sink.asExpr() = fc.getArgument(0) and fc.getTarget().hasName("strlen"))
  }
}

from TaintedFormatConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
select sink, source, sink, "Taint from fgets call in " + source.getNode().getFunction().getFile().getBaseName()

您可以在 https://lgtm.com/query/4800800615370766111/ 查看结果.

关于c - Semmle QL : TaintTracking hasFlow() Problem with Sources that taint their Arguments,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58164464/

相关文章:

c - 使用类型转换删除 gcc 编译器警告

c - 平台独立的方式来定位字体

c - 如何处理线程内发生的错误

c++ - 与 Windows GUI 交互

c - strtok 无法标记化?

c - 静态代码分析器无法识别正在使用的 C int 类型