Skip to content

Commit b3dbe20

Browse files
committed
adding powershell cmd injection critical query, updated unit test results
1 parent a5a632f commit b3dbe20

File tree

4 files changed

+229
-92
lines changed

4 files changed

+229
-92
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>Code that passes user input directly to
7+
<code>Invoke-Expression</code>, <code>&amp;</code>, or some other library
8+
routine that executes a command, allows the user to execute malicious
9+
code.</p>
10+
11+
<p>The following are considered dangerous sinks: </p>
12+
<ul>
13+
<li>Invoke-Expression</li>
14+
<li>InvokeScript</li>
15+
<li>CreateNestedPipeline</li>
16+
<li>AddScript</li>
17+
<li>powershell</li>
18+
<li>cmd</li>
19+
<li>Foreach-Object</li>
20+
<li>Invoke</li>
21+
<li>CreateScriptBlock</li>
22+
<li>NewScriptBlock</li>
23+
<li>ExpandString</li>
24+
</ul>
25+
26+
</overview>
27+
<recommendation>
28+
29+
<p>If possible, use hard-coded string literals to specify the command to run
30+
or library to load. Instead of passing the user input directly to the
31+
process or library function, examine the user input and then choose
32+
among hard-coded string literals.</p>
33+
34+
<p>If the applicable libraries or commands cannot be determined at
35+
compile time, then add code to verify that the user input string is
36+
safe before using it.</p>
37+
38+
</recommendation>
39+
<example>
40+
41+
<p>The following example shows code that takes a shell script that can be changed
42+
maliciously by a user, and passes it straight to <code>Invoke-Expression</code>
43+
without examining it first.</p>
44+
45+
<sample src="examples/command_injection.ps1" />
46+
47+
</example>
48+
<references>
49+
50+
<li>
51+
OWASP:
52+
<a href="https://www.owasp.org/index.php/Command_Injection">Command Injection</a>.
53+
</li>
54+
<li>
55+
Injection Hunter:
56+
<a href="https://devblogs.microsoft.com/powershell/powershell-injection-hunter-security-auditing-for-powershell-scripts/">PowerShell Injection Hunter: Security Auditing for PowerShell Scripts</a>.
57+
</li>
58+
<!-- LocalWords: CWE untrusted unsanitized Runtime
59+
-->
60+
61+
62+
</references>
63+
</qhelp>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* @name Uncontrolled command line from param to CmdletBinding
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/microsoft/public/command-injection-critical
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.CommandInjectionCustomizations::CommandInjection
18+
import semmle.code.powershell.dataflow.TaintTracking
19+
import semmle.code.powershell.dataflow.DataFlow
20+
21+
abstract class CriticalSource extends DataFlow::Node {
22+
/** Gets a string that describes the type of this flow source. */
23+
abstract string getSourceType();
24+
}
25+
26+
class CmdletBindingParam extends CriticalSource {
27+
CmdletBindingParam(){
28+
exists(Attribute a, Function f |
29+
a.getName() = "CmdletBinding" and
30+
f = a.getEnclosingFunction() and
31+
this.asParameter() = f.getAParameter()
32+
)
33+
}
34+
override string getSourceType(){
35+
result = "param to CmdletBinding function"
36+
}
37+
}
38+
39+
40+
private module Config implements DataFlow::ConfigSig {
41+
predicate isSource(DataFlow::Node source) { source instanceof CriticalSource }
42+
43+
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
44+
45+
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
46+
}
47+
48+
/**
49+
* Taint-tracking for reasoning about command-injection vulnerabilities.
50+
*/
51+
module CommandInjectionCriticalFlow = TaintTracking::Global<Config>;
52+
import CommandInjectionCriticalFlow::PathGraph
53+
54+
from CommandInjectionCriticalFlow::PathNode source, CommandInjectionCriticalFlow::PathNode sink, CriticalSource sourceNode
55+
where
56+
CommandInjectionCriticalFlow::flowPath(source, sink) and
57+
sourceNode = source.getNode()
58+
select sink.getNode(), source, sink, "This command depends on a $@.", sourceNode,
59+
sourceNode.getSourceType()

0 commit comments

Comments
 (0)