Skip to content

Commit 299c3e9

Browse files
committed
PS: Add 'powershell/command-injection' query.
1 parent 0fb75af commit 299c3e9

File tree

3 files changed

+110
-0
lines changed

3 files changed

+110
-0
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Provides default sources, sinks and sanitizers for reasoning about
3+
* command-injection vulnerabilities, as well as extension points for
4+
* adding your own.
5+
*/
6+
7+
private import semmle.code.powershell.dataflow.DataFlow
8+
private import semmle.code.powershell.dataflow.flowsources.FlowSources
9+
private import semmle.code.powershell.Cfg
10+
11+
module CommandInjection {
12+
/**
13+
* A data flow source for command-injection vulnerabilities.
14+
*/
15+
abstract class Source extends DataFlow::Node {
16+
/** Gets a string that describes the type of this flow source. */
17+
abstract string getSourceType();
18+
}
19+
20+
/**
21+
* A data flow sink for command-injection vulnerabilities.
22+
*/
23+
abstract class Sink extends DataFlow::Node { }
24+
25+
/**
26+
* A sanitizer for command-injection vulnerabilities.
27+
*/
28+
abstract class Sanitizer extends DataFlow::Node { }
29+
30+
/** A source of user input, considered as a flow source for command injection. */
31+
class FlowSourceAsSource extends Source instanceof SourceNode {
32+
override string getSourceType() { result = "user-provided value" }
33+
}
34+
35+
/**
36+
* A command argument to a function that initiates an operating system command.
37+
*/
38+
class SystemCommandExecutionSink extends Sink {
39+
SystemCommandExecutionSink() {
40+
// An argument to a call
41+
exists(DataFlow::CallNode call |
42+
call.getName() = "Invoke-Expression"
43+
or
44+
call instanceof DataFlow::CallOperatorNode
45+
|
46+
call.getAnArgument() = this
47+
)
48+
or
49+
// Or the call command itself in case it's a use of operator &.
50+
any(DataFlow::CallOperatorNode call).getCommand() = this
51+
}
52+
}
53+
54+
private class ExternalCommandInjectionSink extends Sink {
55+
ExternalCommandInjectionSink() {
56+
this = ModelOutput::getASinkNode("command-injection").asSink()
57+
}
58+
}
59+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Provides a taint tracking configuration for reasoning about
3+
* command-injection vulnerabilities (CWE-078).
4+
*
5+
* Note, for performance reasons: only import this file if
6+
* `CommandInjectionFlow` is needed, otherwise
7+
* `CommandInjectionCustomizations` should be imported instead.
8+
*/
9+
10+
import powershell
11+
import semmle.code.powershell.dataflow.TaintTracking
12+
import CommandInjectionCustomizations::CommandInjection
13+
import semmle.code.powershell.dataflow.DataFlow
14+
15+
private module Config implements DataFlow::ConfigSig {
16+
predicate isSource(DataFlow::Node source) { source instanceof Source }
17+
18+
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
19+
20+
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
21+
}
22+
23+
/**
24+
* Taint-tracking for reasoning about command-injection vulnerabilities.
25+
*/
26+
module CommandInjectionFlow = TaintTracking::Global<Config>;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* @name Uncontrolled command line
3+
* @description Using externally controlled strings in a command line may allow a malicious
4+
* user to change the meaning of the command.
5+
* @kind path-problem
6+
* @problem.severity error
7+
* @security-severity 9.8
8+
* @precision high
9+
* @id powershell/command-injection
10+
* @tags correctness
11+
* security
12+
* external/cwe/cwe-078
13+
* external/cwe/cwe-088
14+
*/
15+
16+
import powershell
17+
import semmle.code.powershell.security.CommandInjectionQuery
18+
import CommandInjectionFlow::PathGraph
19+
20+
from CommandInjectionFlow::PathNode source, CommandInjectionFlow::PathNode sink, Source sourceNode
21+
where
22+
CommandInjectionFlow::flowPath(source, sink) and
23+
sourceNode = source.getNode()
24+
select sink.getNode(), source, sink, "This command depends on a $@.", sourceNode,
25+
sourceNode.getSourceType()

0 commit comments

Comments
 (0)