Semmle QL:TaintTracking hasFlow() 污染其参数的来源问题
Semmle QL: TaintTracking hasFlow() Problem with Sources that taint their Arguments
我想对使用用户输入污染其参数的函数进行 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()
仍然 returns 什么都没有 - 尽管路径肯定存在。
有谁知道我在上面的查询中可能做错了什么?
缺少的位在 isSource
中,其中污点跟踪从第 0 个参数开始到 fgets
。使用 asExpr
将描述从该参数 到 fgets
的流程。我们想要的是 out of fgets 通过该参数的流程。您可以通过将 asExpr
替换为 asDefiningArgument
.
来获得它
Here 是您查询结果的 link,我在 isSource
中同时使用了 asExpr
和 asDefiningArgument
。这意味着如果您将来扩展 isUserInput
,它的表达式将被视为来源,既作为它们的值又作为输出参数。
新版查询有8个结果,有些只看source和sink会比较难理解,因为它们可以在不同的文件中。我制作了一个清理版本的查询
- 生成源和汇之间的路径解释 (
@kind path-problem
),
- 删除了辅助谓词的
IsTaintedArg
class 包装,
- 删除了一些未使用的参数并进行了检查,并且
- 将调用添加到
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/ 查看结果。
我想对使用用户输入污染其参数的函数进行 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()
仍然 returns 什么都没有 - 尽管路径肯定存在。
有谁知道我在上面的查询中可能做错了什么?
缺少的位在 isSource
中,其中污点跟踪从第 0 个参数开始到 fgets
。使用 asExpr
将描述从该参数 到 fgets
的流程。我们想要的是 out of fgets 通过该参数的流程。您可以通过将 asExpr
替换为 asDefiningArgument
.
Here 是您查询结果的 link,我在 isSource
中同时使用了 asExpr
和 asDefiningArgument
。这意味着如果您将来扩展 isUserInput
,它的表达式将被视为来源,既作为它们的值又作为输出参数。
新版查询有8个结果,有些只看source和sink会比较难理解,因为它们可以在不同的文件中。我制作了一个清理版本的查询
- 生成源和汇之间的路径解释 (
@kind path-problem
), - 删除了辅助谓词的
IsTaintedArg
class 包装, - 删除了一些未使用的参数并进行了检查,并且
- 将调用添加到
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/ 查看结果。