mirror of
https://github.com/github/codeql.git
synced 2025-12-23 20:26:32 +01:00
Merge pull request #12338 from MathiasVP/expr-sanitizer-for-exec-tainted
C++: Speedup `cpp/command-line-injection`
This commit is contained in:
@@ -25,15 +25,15 @@ import semmle.code.cpp.models.implementations.Strcat
|
|||||||
import DataFlow::PathGraph
|
import DataFlow::PathGraph
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `fst` is a string that is used in a format or concatenation function resulting in `snd`,
|
* Holds if `incoming` is a string that is used in a format or concatenation function resulting
|
||||||
* and is *not* placed at the start of the resulting string. This indicates that the author did not
|
* in `outgoing`, and is *not* placed at the start of the resulting string. This indicates that
|
||||||
* expect `fst` to control what program is run if the resulting string is eventually interpreted as
|
* the author did not expect `incoming` to control what program is run if the resulting string
|
||||||
* a command line, for example as an argument to `system`.
|
* is eventually interpreted as a command line, for example as an argument to `system`.
|
||||||
*/
|
*/
|
||||||
predicate interestingConcatenation(DataFlow::Node fst, DataFlow::Node snd) {
|
predicate interestingConcatenation(DataFlow::Node incoming, DataFlow::Node outgoing) {
|
||||||
exists(FormattingFunctionCall call, int index, FormatLiteral literal |
|
exists(FormattingFunctionCall call, int index, FormatLiteral literal |
|
||||||
fst.asIndirectArgument() = call.getConversionArgument(index) and
|
incoming.asIndirectArgument() = call.getConversionArgument(index) and
|
||||||
snd.asDefiningArgument() = call.getOutputArgument(false) and
|
outgoing.asDefiningArgument() = call.getOutputArgument(false) and
|
||||||
literal = call.getFormat() and
|
literal = call.getFormat() and
|
||||||
not literal.getConvSpecOffset(index) = 0 and
|
not literal.getConvSpecOffset(index) = 0 and
|
||||||
literal.getConversionChar(index) = ["s", "S"]
|
literal.getConversionChar(index) = ["s", "S"]
|
||||||
@@ -42,16 +42,16 @@ predicate interestingConcatenation(DataFlow::Node fst, DataFlow::Node snd) {
|
|||||||
// strcat and friends
|
// strcat and friends
|
||||||
exists(StrcatFunction strcatFunc, Call call |
|
exists(StrcatFunction strcatFunc, Call call |
|
||||||
call.getTarget() = strcatFunc and
|
call.getTarget() = strcatFunc and
|
||||||
fst.asIndirectArgument() = call.getArgument(strcatFunc.getParamSrc()) and
|
incoming.asIndirectArgument() = call.getArgument(strcatFunc.getParamSrc()) and
|
||||||
snd.asDefiningArgument() = call.getArgument(strcatFunc.getParamDest())
|
outgoing.asDefiningArgument() = call.getArgument(strcatFunc.getParamDest())
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Call call, Operator op |
|
exists(Call call, Operator op |
|
||||||
call.getTarget() = op and
|
call.getTarget() = op and
|
||||||
op.hasQualifiedName("std", "operator+") and
|
op.hasQualifiedName("std", "operator+") and
|
||||||
op.getType().(UserType).hasQualifiedName("std", "basic_string") and
|
op.getType().(UserType).hasQualifiedName("std", "basic_string") and
|
||||||
fst.asIndirectArgument() = call.getArgument(1) and // left operand
|
incoming.asIndirectArgument() = call.getArgument(1) and // left operand
|
||||||
call = snd.asInstruction().getUnconvertedResultExpression()
|
call = outgoing.asInstruction().getUnconvertedResultExpression()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,22 +60,23 @@ class ConcatState extends DataFlow::FlowState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ExecState extends DataFlow::FlowState {
|
class ExecState extends DataFlow::FlowState {
|
||||||
DataFlow::Node fst;
|
DataFlow::Node incoming;
|
||||||
DataFlow::Node snd;
|
DataFlow::Node outgoing;
|
||||||
|
|
||||||
ExecState() {
|
ExecState() {
|
||||||
this =
|
this =
|
||||||
"ExecState (" + fst.getLocation() + " | " + fst + ", " + snd.getLocation() + " | " + snd + ")" and
|
"ExecState (" + incoming.getLocation() + " | " + incoming + ", " + outgoing.getLocation() +
|
||||||
interestingConcatenation(pragma[only_bind_into](fst), pragma[only_bind_into](snd))
|
" | " + outgoing + ")" and
|
||||||
|
interestingConcatenation(pragma[only_bind_into](incoming), pragma[only_bind_into](outgoing))
|
||||||
}
|
}
|
||||||
|
|
||||||
DataFlow::Node getFstNode() { result = fst }
|
DataFlow::Node getIncomingNode() { result = incoming }
|
||||||
|
|
||||||
DataFlow::Node getSndNode() { result = snd }
|
DataFlow::Node getOutgoingNode() { result = outgoing }
|
||||||
|
|
||||||
/** Holds if this is a possible `ExecState` for `sink`. */
|
/** Holds if this is a possible `ExecState` for `sink`. */
|
||||||
predicate isFeasibleForSink(DataFlow::Node sink) {
|
predicate isFeasibleForSink(DataFlow::Node sink) {
|
||||||
any(ExecStateConfiguration conf).hasFlow(snd, sink)
|
any(ExecStateConfiguration conf).hasFlow(outgoing, sink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,6 +85,12 @@ predicate isSinkImpl(DataFlow::Node sink, Expr command, string callChain) {
|
|||||||
shellCommand(command, callChain)
|
shellCommand(command, callChain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
predicate isSanitizerImpl(DataFlow::Node node) {
|
||||||
|
node.asExpr().getUnspecifiedType() instanceof IntegralType
|
||||||
|
or
|
||||||
|
node.asExpr().getUnspecifiedType() instanceof FloatingPointType
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `TaintTracking` configuration that's used to find the relevant `ExecState`s for a
|
* A `TaintTracking` configuration that's used to find the relevant `ExecState`s for a
|
||||||
* given sink. This avoids a cartesian product between all sinks and all `ExecState`s in
|
* given sink. This avoids a cartesian product between all sinks and all `ExecState`s in
|
||||||
@@ -93,11 +100,13 @@ class ExecStateConfiguration extends TaintTracking2::Configuration {
|
|||||||
ExecStateConfiguration() { this = "ExecStateConfiguration" }
|
ExecStateConfiguration() { this = "ExecStateConfiguration" }
|
||||||
|
|
||||||
override predicate isSource(DataFlow::Node source) {
|
override predicate isSource(DataFlow::Node source) {
|
||||||
exists(ExecState state | state.getSndNode() = source)
|
any(ExecState state).getOutgoingNode() = source
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate isSink(DataFlow::Node sink) { isSinkImpl(sink, _, _) }
|
override predicate isSink(DataFlow::Node sink) { isSinkImpl(sink, _, _) }
|
||||||
|
|
||||||
|
override predicate isSanitizer(DataFlow::Node node) { isSanitizerImpl(node) }
|
||||||
|
|
||||||
override predicate isSanitizerOut(DataFlow::Node node) {
|
override predicate isSanitizerOut(DataFlow::Node node) {
|
||||||
isSink(node, _) // Prevent duplicates along a call chain, since `shellCommand` will include wrappers
|
isSink(node, _) // Prevent duplicates along a call chain, since `shellCommand` will include wrappers
|
||||||
}
|
}
|
||||||
@@ -121,18 +130,11 @@ class ExecTaintConfiguration extends TaintTracking::Configuration {
|
|||||||
DataFlow::FlowState state2
|
DataFlow::FlowState state2
|
||||||
) {
|
) {
|
||||||
state1 instanceof ConcatState and
|
state1 instanceof ConcatState and
|
||||||
state2.(ExecState).getFstNode() = node1 and
|
state2.(ExecState).getIncomingNode() = node1 and
|
||||||
state2.(ExecState).getSndNode() = node2
|
state2.(ExecState).getOutgoingNode() = node2
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) {
|
override predicate isSanitizer(DataFlow::Node node) { isSanitizerImpl(node) }
|
||||||
(
|
|
||||||
node.asInstruction().getResultType() instanceof IntegralType
|
|
||||||
or
|
|
||||||
node.asInstruction().getResultType() instanceof FloatingPointType
|
|
||||||
) and
|
|
||||||
state instanceof ConcatState
|
|
||||||
}
|
|
||||||
|
|
||||||
override predicate isSanitizerOut(DataFlow::Node node) {
|
override predicate isSanitizerOut(DataFlow::Node node) {
|
||||||
isSink(node, _) // Prevent duplicates along a call chain, since `shellCommand` will include wrappers
|
isSink(node, _) // Prevent duplicates along a call chain, since `shellCommand` will include wrappers
|
||||||
@@ -146,7 +148,7 @@ where
|
|||||||
conf.hasFlowPath(sourceNode, sinkNode) and
|
conf.hasFlowPath(sourceNode, sinkNode) and
|
||||||
taintCause = sourceNode.getNode().(FlowSource).getSourceType() and
|
taintCause = sourceNode.getNode().(FlowSource).getSourceType() and
|
||||||
isSinkImpl(sinkNode.getNode(), command, callChain) and
|
isSinkImpl(sinkNode.getNode(), command, callChain) and
|
||||||
concatResult = sinkNode.getState().(ExecState).getSndNode()
|
concatResult = sinkNode.getState().(ExecState).getOutgoingNode()
|
||||||
select command, sourceNode, sinkNode,
|
select command, sourceNode, sinkNode,
|
||||||
"This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to "
|
"This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to "
|
||||||
+ callChain + ".", sourceNode, "user input (" + taintCause + ")", concatResult,
|
+ callChain + ".", sourceNode, "user input (" + taintCause + ")", concatResult,
|
||||||
|
|||||||
Reference in New Issue
Block a user