merged work into CommandInjection query

This commit is contained in:
Chanel Young
2025-04-16 14:32:30 -07:00
parent 2266cd2eb8
commit ed553d393b
10 changed files with 392 additions and 637 deletions

View File

@@ -5,6 +5,7 @@
*/
private import semmle.code.powershell.dataflow.DataFlow
import semmle.code.powershell.ApiGraphs
private import semmle.code.powershell.dataflow.flowsources.FlowSources
private import semmle.code.powershell.Cfg
@@ -20,7 +21,9 @@ module CommandInjection {
/**
* A data flow sink for command-injection vulnerabilities.
*/
abstract class Sink extends DataFlow::Node { }
abstract class Sink extends DataFlow::Node {
abstract string getSinkType();
}
/**
* A sanitizer for command-injection vulnerabilities.
@@ -39,13 +42,16 @@ module CommandInjection {
SystemCommandExecutionSink() {
// An argument to a call
exists(DataFlow::CallNode call |
call.getName() = "Invoke-Expression" and
call.getName() = ["Invoke-Expression", "iex"] and
call.getAnArgument() = this
)
or
// Or the call command itself in case it's a use of operator &.
any(DataFlow::CallOperatorNode call).getCommand() = this
}
override string getSinkType() {
result = "call to Invoke-Expression"
}
}
class AddTypeSink extends Sink {
@@ -55,11 +61,169 @@ module CommandInjection {
call.getAnArgument() = this
)
}
override string getSinkType() {
result = "call to Add-Type"
}
}
class InvokeScriptSink extends Sink {
InvokeScriptSink() {
exists(API::Node call |
API::getTopLevelMember("executioncontext").getMember("invokecommand").getMethod("invokescript") = call and
this = call.getArgument(_).asSink()
)
}
override string getSinkType(){
result = "call to InvokeScript"
}
}
class CreateNestedPipelineSink extends Sink {
CreateNestedPipelineSink() {
exists(API::Node call |
API::getTopLevelMember("host").getMember("runspace").getMethod("createnestedpipeline") = call and
this = call.getArgument(_).asSink()
)
}
override string getSinkType(){
result = "call to CreateNestedPipeline"
}
}
class AddScriptInvokeSink extends Sink {
AddScriptInvokeSink() {
exists(InvokeMemberExpr ie |
this.asExpr().getExpr() = ie.getAnArgument() and
ie.getName() = "AddScript" and
ie.getQualifier().(InvokeMemberExpr).getName() = "Create" and
ie.getQualifier().getAChild().toString() = "PowerShell" and
ie.getParent().(InvokeMemberExpr).getName() = "Invoke"
)
}
override string getSinkType(){
result = "call to AddScript"
}
}
class PowershellSink extends Sink {
PowershellSink() {
exists( CmdCall c |
c.getName() = "powershell" |
(
this.asExpr().getExpr() = c.getArgument(1) and
c.getArgument(0).getValue().toString() = "-command"
) or
(
this.asExpr().getExpr() = c.getArgument(0)
)
)
}
override string getSinkType(){
result = "call to Powershell"
}
}
class CmdSink extends Sink {
CmdSink() {
exists(CmdCall c |
this.asExpr().getExpr() = c.getArgument(1) and
c.getName() = "cmd" and
c.getArgument(0).getValue().toString() = "/c"
)
}
override string getSinkType(){
result = "call to Cmd"
}
}
class ForEachObjectSink extends Sink {
ForEachObjectSink() {
exists(CmdCall c |
this.asExpr().getExpr() = c.getAnArgument() and
c.getName() = "Foreach-Object"
)
}
override string getSinkType(){
result = "call to ForEach-Object"
}
}
class InvokeSink extends Sink {
InvokeSink() {
exists(InvokeMemberExpr ie |
this.asExpr().getExpr() = ie.getCallee() or
this.asExpr().getExpr() = ie.getQualifier().getAChild*()
)
}
override string getSinkType(){
result = "call to Invoke"
}
}
class CreateScriptBlockSink extends Sink {
CreateScriptBlockSink() {
exists(InvokeMemberExpr ie |
this.asExpr().getExpr() = ie.getAnArgument() and
ie.getName() = "Create" and
ie.getQualifier().toString() = "ScriptBlock"
)
}
override string getSinkType(){
result = "call to CreateScriptBlock"
}
}
class NewScriptBlockSink extends Sink {
NewScriptBlockSink() {
exists(API::Node call |
API::getTopLevelMember("executioncontext").getMember("invokecommand").getMethod("newscriptblock") = call and
this = call.getArgument(_).asSink()
)
}
override string getSinkType(){
result = "call to NewScriptBlock"
}
}
class ExpandStringSink extends Sink {
ExpandStringSink() {
exists(API::Node call | this = call.getArgument(_).asSink() |
API::getTopLevelMember("executioncontext").getMember("invokecommand").getMethod("expandstring") = call or
API::getTopLevelMember("executioncontext").getMember("sessionstate").getMember("invokecommand").getMethod("expandstring") = call
)
}
override string getSinkType(){
result = "call to ExpandString"
}
}
private class ExternalCommandInjectionSink extends Sink {
ExternalCommandInjectionSink() {
this = ModelOutput::getASinkNode("command-injection").asSink()
}
override string getSinkType() {
result = "external command injection"
}
}
class TypedParameterSanitizer extends Sanitizer {
TypedParameterSanitizer() {
exists(Function f, Parameter p |
p = f.getAParameter() and
p.getStaticType() != "Object" and
this.asParameter() = p
)
}
}
class SingleQuoteSanitizer extends Sanitizer {
SingleQuoteSanitizer() {
exists(Expr e, VarReadAccess v |
e = this.asExpr().getExpr().getParent() and
e.toString().matches("%'$" + v.getVariable().getName() + "'%")
)
}
}
}

View File

@@ -8,6 +8,21 @@
routine that executes a command, allows the user to execute malicious
code.</p>
<p>The following are considered dangerous sinks: </p>
<ul>
<li>Invoke-Expression</li>
<li>InvokeScript</li>
<li>CreateNestedPipeline</li>
<li>AddScript</li>
<li>powershell</li>
<li>cmd</li>
<li>Foreach-Object</li>
<li>Invoke</li>
<li>CreateScriptBlock</li>
<li>NewScriptBlock</li>
<li>ExpandString</li>
</ul>
</overview>
<recommendation>
@@ -36,7 +51,10 @@ without examining it first.</p>
OWASP:
<a href="https://www.owasp.org/index.php/Command_Injection">Command Injection</a>.
</li>
<li>
Injection Hunter:
<a href="https://devblogs.microsoft.com/powershell/powershell-injection-hunter-security-auditing-for-powershell-scripts/">PowerShell Injection Hunter: Security Auditing for PowerShell Scripts</a>.
</li>
<!-- LocalWords: CWE untrusted unsanitized Runtime
-->

View File

@@ -1,26 +0,0 @@
import powershell
import semmle.code.powershell.dataflow.TaintTracking
import semmle.code.powershell.dataflow.DataFlow
import semmle.code.powershell.ApiGraphs
abstract class Sanitizer extends DataFlow::Node {}
class TypedParameterSanitizer extends Sanitizer {
TypedParameterSanitizer() {
exists(Function f, Parameter p |
p = f.getAParameter() and
p.getStaticType() != "Object" and
this.asParameter() = p
)
}
}
class SingleQuoteSanitizer extends Sanitizer {
SingleQuoteSanitizer() {
exists(Expr e, VarReadAccess v |
e = this.asExpr().getExpr().getParent() and
e.toString().matches("%'$" + v.getVariable().getName() + "'%")
)
}
}

View File

@@ -1,152 +0,0 @@
import powershell
import semmle.code.powershell.dataflow.TaintTracking
import semmle.code.powershell.dataflow.DataFlow
import semmle.code.powershell.ApiGraphs
import semmle.code.powershell.dataflow.flowsources.FlowSources
abstract class InjectionSink extends DataFlow::Node {
abstract string getSinkType();
}
class InvokeExpressionCall extends InjectionSink {
InvokeExpressionCall() {
exists(CmdCall c |
this.asExpr().getExpr() = c.getAnArgument() and
c.getName() = ["Invoke-Expression", "iex", "Add-Type" ] )
}
override string getSinkType(){
result = "call to Invoke-Expression"
}
}
class InvokeScriptSink extends InjectionSink {
InvokeScriptSink() {
exists(API::Node call |
API::getTopLevelMember("executioncontext").getMember("invokecommand").getMethod("invokescript") = call and
this = call.getArgument(_).asSink()
)
}
override string getSinkType(){
result = "call to InvokeScript"
}
}
class CreateNestedPipelineSink extends InjectionSink {
CreateNestedPipelineSink() {
exists(API::Node call |
API::getTopLevelMember("host").getMember("runspace").getMethod("createnestedpipeline") = call and
this = call.getArgument(_).asSink()
)
}
override string getSinkType(){
result = "call to CreateNestedPipeline"
}
}
class AddScriptInvokeSink extends InjectionSink {
AddScriptInvokeSink() {
exists(InvokeMemberExpr ie |
this.asExpr().getExpr() = ie.getAnArgument() and
ie.getName() = "AddScript" and
ie.getQualifier().(InvokeMemberExpr).getName() = "Create" and
ie.getQualifier().getAChild().toString() = "PowerShell" and
ie.getParent().(InvokeMemberExpr).getName() = "Invoke"
)
}
override string getSinkType(){
result = "call to AddScript"
}
}
class PowershellSink extends InjectionSink {
PowershellSink() {
exists( CmdCall c |
c.getName() = "powershell" |
(
this.asExpr().getExpr() = c.getArgument(1) and
c.getArgument(0).getValue().toString() = "-command"
) or
(
this.asExpr().getExpr() = c.getArgument(0)
)
)
}
override string getSinkType(){
result = "call to Powershell"
}
}
class CmdSink extends InjectionSink {
CmdSink() {
exists(CmdCall c |
this.asExpr().getExpr() = c.getArgument(1) and
c.getName() = "cmd" and
c.getArgument(0).getValue().toString() = "/c"
)
}
override string getSinkType(){
result = "call to Cmd"
}
}
class ForEachObjectSink extends InjectionSink {
ForEachObjectSink() {
exists(CmdCall c |
this.asExpr().getExpr() = c.getAnArgument() and
c.getName() = "Foreach-Object"
)
}
override string getSinkType(){
result = "call to ForEach-Object"
}
}
class InvokeSink extends InjectionSink {
InvokeSink() {
exists(InvokeMemberExpr ie |
this.asExpr().getExpr() = ie.getCallee() or
this.asExpr().getExpr() = ie.getQualifier().getAChild*()
)
}
override string getSinkType(){
result = "call to Invoke"
}
}
class CreateScriptBlockSink extends InjectionSink {
CreateScriptBlockSink() {
exists(InvokeMemberExpr ie |
this.asExpr().getExpr() = ie.getAnArgument() and
ie.getName() = "Create" and
ie.getQualifier().toString() = "ScriptBlock"
)
}
override string getSinkType(){
result = "call to CreateScriptBlock"
}
}
class NewScriptBlockSink extends InjectionSink {
NewScriptBlockSink() {
exists(API::Node call |
API::getTopLevelMember("executioncontext").getMember("invokecommand").getMethod("newscriptblock") = call and
this = call.getArgument(_).asSink()
)
}
override string getSinkType(){
result = "call to NewScriptBlock"
}
}
class ExpandStringSink extends InjectionSink {
ExpandStringSink() {
exists(API::Node call | this = call.getArgument(_).asSink() |
API::getTopLevelMember("executioncontext").getMember("invokecommand").getMethod("expandstring") = call or
API::getTopLevelMember("executioncontext").getMember("sessionstate").getMember("invokecommand").getMethod("expandstring") = call
)
}
override string getSinkType(){
result = "call to ExpandString"
}
}

View File

@@ -1,48 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Code that passes user input directly to
<code>Invoke-Expression</code>, <code>&</code>, or some other library
routine that executes a command, allows the user to execute malicious
code.</p>
<p>This is a port of the InjectionHunter tool by Lee Holmes, and checks when user input is passed to any of the following:</p>
<ul>
<li>Invoke-Expression</li>
<li>InvokeScript</li>
<li>CreateNestedPipeline</li>
<li>AddScript</li>
<li>powershell</li>
<li>cmd</li>
<li>Foreach-Object</li>
<li>Invoke</li>
<li>CreateScriptBlock</li>
<li>NewScriptBlock</li>
<li>ExpandString</li>
</ul>
</overview>
<recommendation>
<p>Possible script injection risk. Untrusted input can cause arbitrary PowerShell expressions to be run.
Variables may be used directly for dynamic parameter arguments, splatting can be used for dynamic parameter names,
and the invocation operator can be used for dynamic command names. If content escaping is truly needed, PowerShell has several valid quote characters,
so [System.Management.Automation.Language.CodeGeneration]::Escape* should be used.</p>
</recommendation>
<references>
<li>
OWASP:
<a href="https://www.owasp.org/index.php/Command_Injection">Command Injection</a>.
</li>
<li>
Injection Hunter:
<a href="https://devblogs.microsoft.com/powershell/powershell-injection-hunter-security-auditing-for-powershell-scripts/">PowerShell Injection Hunter: Security Auditing for PowerShell Scripts</a>.
</li>
</references>
</qhelp>

View File

@@ -1,36 +0,0 @@
/**
* @name User Input to injection sink
* @description Finding cases where the user input is passed an dangerous method that can lead to RCE
* @kind path-problem
* @problem.severity error
* @security-severity 9.8
* @precision high
* @id powershell/microsoft/public/user-input-to-injection-sink
* @tags security
* external/cwe/cwe-078
* external/cwe/cwe-088
*/
import powershell
import semmle.code.powershell.dataflow.TaintTracking
import semmle.code.powershell.dataflow.DataFlow
import semmle.code.powershell.ApiGraphs
import semmle.code.powershell.dataflow.flowsources.FlowSources
import Sanitizers
import Sinks
private module InjectionConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source instanceof SourceNode
}
predicate isSink(DataFlow::Node sink) { sink instanceof InjectionSink }
predicate isBarrier(DataFlow::Node node) {node instanceof Sanitizer}
}
module InjectionFlow = TaintTracking::Global<InjectionConfig>;
import InjectionFlow::PathGraph
from InjectionFlow::PathNode source, InjectionFlow::PathNode sink
where InjectionFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Possible injection path from user input to dangerous " + sink.getNode().(InjectionSink).getSinkType()

View File

@@ -1,7 +1,210 @@
param ($x)
function Invoke-InvokeExpressionInjection1
{
param($UserInput)
Invoke-Expression "Get-Process -Name $UserInput"
}
Invoke-Expression -Command "Get-Process -Id $x" # BAD
function Invoke-InvokeExpressionInjection2
{
param($UserInput)
iex "Get-Process -Name $UserInput"
}
$code = "$Env:MY_VAR"
function Invoke-InvokeExpressionInjection3
{
param($UserInput)
$executionContext.InvokeCommand.InvokeScript("Get-Process -Name $UserInput")
}
& "$code --enabled" # BAD
function Invoke-InvokeExpressionInjection4
{
param($UserInput)
$host.Runspace.CreateNestedPipeline("Get-Process -Name $UserInput", $false).Invoke()
}
function Invoke-InvokeExpressionInjection5
{
param($UserInput)
[PowerShell]::Create().AddScript("Get-Process -Name $UserInput").Invoke()
}
function Invoke-InvokeExpressionInjection6
{
param($UserInput)
Add-Type "public class Foo { $UserInput }"
}
function Invoke-InvokeExpressionInjection7
{
param($UserInput)
Add-Type -TypeDefinition "public class Foo { $UserInput }"
}
function Invoke-InvokeExpressionInjection8
{
param($UserInput)
$code = "public class Foo { $UserInput }"
Add-Type -TypeDefinition $code
}
function Invoke-InvokeExpressionInjectionFP
{
param($UserInput)
$code = @"
public class BasicTest
{
public static int Add(int a, int b)
{
return (a + b);
}
public int Multiply(int a, int b)
{
return (a * b);
}
}
"@
Add-Type -TypeDefinition $code
}
function Invoke-ExploitableCommandInjection1
{
param($UserInput)
powershell -command "Get-Process -Name $UserInput"
}
function Invoke-ExploitableCommandInjection2
{
param($UserInput)
powershell "Get-Process -Name $UserInput"
}
function Invoke-ExploitableCommandInjection3
{
param($UserInput)
cmd /c "ping $UserInput"
}
function Invoke-ScriptBlockInjection1
{
param($UserInput)
## Often used when making remote connections
$sb = [ScriptBlock]::Create("Get-Process -Name $UserInput")
Invoke-Command RemoteServer $sb
}
function Invoke-ScriptBlockInjection2
{
param($UserInput)
## Often used when making remote connections
$sb = $executionContext.InvokeCommand.NewScriptBlock("Get-Process -Name $UserInput")
Invoke-Command RemoteServer $sb
}
function Invoke-MethodInjection1
{
param($UserInput)
Get-Process | Foreach-Object $UserInput
}
function Invoke-MethodInjection2
{
param($UserInput)
(Get-Process -Id $pid).$UserInput()
}
function Invoke-MethodInjection3
{
param($UserInput)
(Get-Process -Id $pid).$UserInput.Invoke()
}
#TODO: currently a FN
function Invoke-ExpandStringInjection1
{
param($UserInput)
## Used to attempt a variable resolution
$executionContext.InvokeCommand.ExpandString($UserInput)
}
function Invoke-ExpandStringInjection2
{
param($UserInput)
## Used to attempt a variable resolution
$executionContext.SessionState.InvokeCommand.ExpandString($UserInput)
}
$input = Read-Host "enter input"
Invoke-InvokeExpressionInjection1 -UserInput $input
Invoke-InvokeExpressionInjection2 -UserInput $input
Invoke-InvokeExpressionInjection3 -UserInput $input
Invoke-InvokeExpressionInjection4 -UserInput $input
Invoke-InvokeExpressionInjection5 -UserInput $input
Invoke-InvokeExpressionInjection6 -UserInput $input
Invoke-InvokeExpressionInjection7 -UserInput $input
Invoke-InvokeExpressionInjection8 -UserInput $input
Invoke-InvokeExpressionInjectionFP -UserInput $input
Invoke-ExploitableCommandInjection1 -UserInput $input
Invoke-ExploitableCommandInjection2 -UserInput $input
Invoke-ExploitableCommandInjection3 -UserInput $input
Invoke-ScriptBlockInjection1 -UserInput $input
Invoke-ScriptBlockInjection2 -UserInput $input
Invoke-MethodInjection1 -UserInput $input
Invoke-MethodInjection2 -UserInput $input
Invoke-MethodInjection3 -UserInput $input
Invoke-PropertyInjection -UserInput $input
Invoke-ExpandStringInjection1 -UserInput $input
Invoke-ExpandStringInjection2 -UserInput $input
#typed input
function Invoke-InvokeExpressionInjectionSafe1
{
param([int] $UserInput)
Invoke-Expression "Get-Process -Name $UserInput"
}
#single quotes to treat them as string literal
function Invoke-InvokeExpressionInjectionSafe2
{
param($UserInput)
Invoke-Expression "Get-Process -Name '$UserInput'"
}
#EscapeSingleQuotedStringContent API
function Invoke-InvokeExpressionInjectionSafe3
{
param([int] $UserInput)
$UserInputClean = [System.Management.Automation.Language.CodeGeneration]::
EscapeSingleQuotedStringContent("$UserInput")
Invoke-Expression "Get-Process -Name $UserInputClean"
}
#EscapeSingleQuotedStringContent API 2
function Invoke-InvokeExpressionInjectionSafe4
{
param([int] $UserInput)
$UserInputClean = [System.Management.Automation.Language.CodeGeneration]::EscapeSingleQuotedStringContent("$UserInput")
Invoke-Expression "Get-Process -Name $UserInputClean"
}
Invoke-InvokeExpressionInjectionSafe1 -UserInput $input
Invoke-InvokeExpressionInjectionSafe2 -UserInput $input
Invoke-InvokeExpressionInjectionSafe3 -UserInput $input
Invoke-InvokeExpressionInjectionSafe4 -UserInput $input

View File

@@ -1,146 +0,0 @@
edges
| test.ps1:3:11:3:20 | UserInput | test.ps1:4:23:4:52 | Get-Process -Name $UserInput | provenance | |
| test.ps1:9:11:9:20 | UserInput | test.ps1:10:9:10:38 | Get-Process -Name $UserInput | provenance | |
| test.ps1:15:11:15:20 | UserInput | test.ps1:16:50:16:79 | Get-Process -Name $UserInput | provenance | |
| test.ps1:21:11:21:20 | UserInput | test.ps1:22:41:22:70 | Get-Process -Name $UserInput | provenance | |
| test.ps1:21:11:21:20 | UserInput | test.ps1:22:60:22:69 | UserInput | provenance | |
| test.ps1:27:11:27:20 | UserInput | test.ps1:28:38:28:67 | Get-Process -Name $UserInput | provenance | |
| test.ps1:27:11:27:20 | UserInput | test.ps1:28:57:28:66 | UserInput | provenance | |
| test.ps1:33:11:33:20 | UserInput | test.ps1:34:14:34:46 | public class Foo { $UserInput } | provenance | |
| test.ps1:39:11:39:20 | UserInput | test.ps1:40:30:40:62 | public class Foo { $UserInput } | provenance | |
| test.ps1:45:11:45:20 | UserInput | test.ps1:48:30:48:34 | code | provenance | |
| test.ps1:73:11:73:20 | UserInput | test.ps1:75:25:75:54 | Get-Process -Name $UserInput | provenance | |
| test.ps1:80:11:80:20 | UserInput | test.ps1:82:16:82:45 | Get-Process -Name $UserInput | provenance | |
| test.ps1:87:11:87:20 | UserInput | test.ps1:89:12:89:28 | ping $UserInput | provenance | |
| test.ps1:102:11:102:20 | UserInput | test.ps1:106:33:106:62 | Get-Process -Name $UserInput | provenance | |
| test.ps1:112:11:112:20 | UserInput | test.ps1:116:58:116:87 | Get-Process -Name $UserInput | provenance | |
| test.ps1:122:11:122:20 | UserInput | test.ps1:124:34:124:43 | UserInput | provenance | |
| test.ps1:129:11:129:20 | UserInput | test.ps1:131:28:131:37 | UserInput | provenance | |
| test.ps1:136:11:136:20 | UserInput | test.ps1:138:28:138:37 | UserInput | provenance | |
| test.ps1:165:11:165:20 | UserInput | test.ps1:168:50:168:59 | UserInput | provenance | |
| test.ps1:173:11:173:20 | UserInput | test.ps1:176:63:176:72 | UserInput | provenance | |
| test.ps1:189:11:189:20 | UserInput | test.ps1:192:23:192:54 | Get-Process -Name "$escaped" | provenance | |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:197:46:197:51 | input | provenance | Src:MaD:11464 |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:198:46:198:51 | input | provenance | Src:MaD:11464 |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:199:46:199:51 | input | provenance | Src:MaD:11464 |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:200:46:200:51 | input | provenance | Src:MaD:11464 |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:201:46:201:51 | input | provenance | Src:MaD:11464 |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:202:46:202:51 | input | provenance | Src:MaD:11464 |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:203:46:203:51 | input | provenance | Src:MaD:11464 |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:204:46:204:51 | input | provenance | Src:MaD:11464 |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:206:48:206:53 | input | provenance | Src:MaD:11464 |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:207:48:207:53 | input | provenance | Src:MaD:11464 |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:208:48:208:53 | input | provenance | Src:MaD:11464 |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:210:41:210:46 | input | provenance | Src:MaD:11464 |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:211:41:211:46 | input | provenance | Src:MaD:11464 |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:212:36:212:41 | input | provenance | Src:MaD:11464 |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:213:36:213:41 | input | provenance | Src:MaD:11464 |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:214:36:214:41 | input | provenance | Src:MaD:11464 |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:218:42:218:47 | input | provenance | Src:MaD:11464 |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:219:42:219:47 | input | provenance | Src:MaD:11464 |
| test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:221:33:221:38 | input | provenance | Src:MaD:11464 |
| test.ps1:197:46:197:51 | input | test.ps1:3:11:3:20 | UserInput | provenance | |
| test.ps1:198:46:198:51 | input | test.ps1:9:11:9:20 | UserInput | provenance | |
| test.ps1:199:46:199:51 | input | test.ps1:15:11:15:20 | UserInput | provenance | |
| test.ps1:200:46:200:51 | input | test.ps1:21:11:21:20 | UserInput | provenance | |
| test.ps1:201:46:201:51 | input | test.ps1:27:11:27:20 | UserInput | provenance | |
| test.ps1:202:46:202:51 | input | test.ps1:33:11:33:20 | UserInput | provenance | |
| test.ps1:203:46:203:51 | input | test.ps1:39:11:39:20 | UserInput | provenance | |
| test.ps1:204:46:204:51 | input | test.ps1:45:11:45:20 | UserInput | provenance | |
| test.ps1:206:48:206:53 | input | test.ps1:73:11:73:20 | UserInput | provenance | |
| test.ps1:207:48:207:53 | input | test.ps1:80:11:80:20 | UserInput | provenance | |
| test.ps1:208:48:208:53 | input | test.ps1:87:11:87:20 | UserInput | provenance | |
| test.ps1:210:41:210:46 | input | test.ps1:102:11:102:20 | UserInput | provenance | |
| test.ps1:211:41:211:46 | input | test.ps1:112:11:112:20 | UserInput | provenance | |
| test.ps1:212:36:212:41 | input | test.ps1:122:11:122:20 | UserInput | provenance | |
| test.ps1:213:36:213:41 | input | test.ps1:129:11:129:20 | UserInput | provenance | |
| test.ps1:214:36:214:41 | input | test.ps1:136:11:136:20 | UserInput | provenance | |
| test.ps1:218:42:218:47 | input | test.ps1:165:11:165:20 | UserInput | provenance | |
| test.ps1:219:42:219:47 | input | test.ps1:173:11:173:20 | UserInput | provenance | |
| test.ps1:221:33:221:38 | input | test.ps1:189:11:189:20 | UserInput | provenance | |
nodes
| test.ps1:3:11:3:20 | UserInput | semmle.label | UserInput |
| test.ps1:4:23:4:52 | Get-Process -Name $UserInput | semmle.label | Get-Process -Name $UserInput |
| test.ps1:9:11:9:20 | UserInput | semmle.label | UserInput |
| test.ps1:10:9:10:38 | Get-Process -Name $UserInput | semmle.label | Get-Process -Name $UserInput |
| test.ps1:15:11:15:20 | UserInput | semmle.label | UserInput |
| test.ps1:16:50:16:79 | Get-Process -Name $UserInput | semmle.label | Get-Process -Name $UserInput |
| test.ps1:21:11:21:20 | UserInput | semmle.label | UserInput |
| test.ps1:22:41:22:70 | Get-Process -Name $UserInput | semmle.label | Get-Process -Name $UserInput |
| test.ps1:22:60:22:69 | UserInput | semmle.label | UserInput |
| test.ps1:27:11:27:20 | UserInput | semmle.label | UserInput |
| test.ps1:28:38:28:67 | Get-Process -Name $UserInput | semmle.label | Get-Process -Name $UserInput |
| test.ps1:28:57:28:66 | UserInput | semmle.label | UserInput |
| test.ps1:33:11:33:20 | UserInput | semmle.label | UserInput |
| test.ps1:34:14:34:46 | public class Foo { $UserInput } | semmle.label | public class Foo { $UserInput } |
| test.ps1:39:11:39:20 | UserInput | semmle.label | UserInput |
| test.ps1:40:30:40:62 | public class Foo { $UserInput } | semmle.label | public class Foo { $UserInput } |
| test.ps1:45:11:45:20 | UserInput | semmle.label | UserInput |
| test.ps1:48:30:48:34 | code | semmle.label | code |
| test.ps1:73:11:73:20 | UserInput | semmle.label | UserInput |
| test.ps1:75:25:75:54 | Get-Process -Name $UserInput | semmle.label | Get-Process -Name $UserInput |
| test.ps1:80:11:80:20 | UserInput | semmle.label | UserInput |
| test.ps1:82:16:82:45 | Get-Process -Name $UserInput | semmle.label | Get-Process -Name $UserInput |
| test.ps1:87:11:87:20 | UserInput | semmle.label | UserInput |
| test.ps1:89:12:89:28 | ping $UserInput | semmle.label | ping $UserInput |
| test.ps1:102:11:102:20 | UserInput | semmle.label | UserInput |
| test.ps1:106:33:106:62 | Get-Process -Name $UserInput | semmle.label | Get-Process -Name $UserInput |
| test.ps1:112:11:112:20 | UserInput | semmle.label | UserInput |
| test.ps1:116:58:116:87 | Get-Process -Name $UserInput | semmle.label | Get-Process -Name $UserInput |
| test.ps1:122:11:122:20 | UserInput | semmle.label | UserInput |
| test.ps1:124:34:124:43 | UserInput | semmle.label | UserInput |
| test.ps1:129:11:129:20 | UserInput | semmle.label | UserInput |
| test.ps1:131:28:131:37 | UserInput | semmle.label | UserInput |
| test.ps1:136:11:136:20 | UserInput | semmle.label | UserInput |
| test.ps1:138:28:138:37 | UserInput | semmle.label | UserInput |
| test.ps1:165:11:165:20 | UserInput | semmle.label | UserInput |
| test.ps1:168:50:168:59 | UserInput | semmle.label | UserInput |
| test.ps1:173:11:173:20 | UserInput | semmle.label | UserInput |
| test.ps1:176:63:176:72 | UserInput | semmle.label | UserInput |
| test.ps1:189:11:189:20 | UserInput | semmle.label | UserInput |
| test.ps1:192:23:192:54 | Get-Process -Name "$escaped" | semmle.label | Get-Process -Name "$escaped" |
| test.ps1:195:10:195:32 | Call to Read-Host | semmle.label | Call to Read-Host |
| test.ps1:197:46:197:51 | input | semmle.label | input |
| test.ps1:198:46:198:51 | input | semmle.label | input |
| test.ps1:199:46:199:51 | input | semmle.label | input |
| test.ps1:200:46:200:51 | input | semmle.label | input |
| test.ps1:201:46:201:51 | input | semmle.label | input |
| test.ps1:202:46:202:51 | input | semmle.label | input |
| test.ps1:203:46:203:51 | input | semmle.label | input |
| test.ps1:204:46:204:51 | input | semmle.label | input |
| test.ps1:206:48:206:53 | input | semmle.label | input |
| test.ps1:207:48:207:53 | input | semmle.label | input |
| test.ps1:208:48:208:53 | input | semmle.label | input |
| test.ps1:210:41:210:46 | input | semmle.label | input |
| test.ps1:211:41:211:46 | input | semmle.label | input |
| test.ps1:212:36:212:41 | input | semmle.label | input |
| test.ps1:213:36:213:41 | input | semmle.label | input |
| test.ps1:214:36:214:41 | input | semmle.label | input |
| test.ps1:218:42:218:47 | input | semmle.label | input |
| test.ps1:219:42:219:47 | input | semmle.label | input |
| test.ps1:221:33:221:38 | input | semmle.label | input |
subpaths
#select
| test.ps1:4:23:4:52 | Get-Process -Name $UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:4:23:4:52 | Get-Process -Name $UserInput | Possible injection path from user input to dangerous call to Invoke-Expression |
| test.ps1:10:9:10:38 | Get-Process -Name $UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:10:9:10:38 | Get-Process -Name $UserInput | Possible injection path from user input to dangerous call to Invoke-Expression |
| test.ps1:16:50:16:79 | Get-Process -Name $UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:16:50:16:79 | Get-Process -Name $UserInput | Possible injection path from user input to dangerous call to InvokeScript |
| test.ps1:22:41:22:70 | Get-Process -Name $UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:22:41:22:70 | Get-Process -Name $UserInput | Possible injection path from user input to dangerous call to CreateNestedPipeline |
| test.ps1:22:41:22:70 | Get-Process -Name $UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:22:41:22:70 | Get-Process -Name $UserInput | Possible injection path from user input to dangerous call to Invoke |
| test.ps1:22:60:22:69 | UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:22:60:22:69 | UserInput | Possible injection path from user input to dangerous call to Invoke |
| test.ps1:28:38:28:67 | Get-Process -Name $UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:28:38:28:67 | Get-Process -Name $UserInput | Possible injection path from user input to dangerous call to AddScript |
| test.ps1:28:38:28:67 | Get-Process -Name $UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:28:38:28:67 | Get-Process -Name $UserInput | Possible injection path from user input to dangerous call to Invoke |
| test.ps1:28:57:28:66 | UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:28:57:28:66 | UserInput | Possible injection path from user input to dangerous call to Invoke |
| test.ps1:34:14:34:46 | public class Foo { $UserInput } | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:34:14:34:46 | public class Foo { $UserInput } | Possible injection path from user input to dangerous call to Invoke-Expression |
| test.ps1:40:30:40:62 | public class Foo { $UserInput } | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:40:30:40:62 | public class Foo { $UserInput } | Possible injection path from user input to dangerous call to Invoke-Expression |
| test.ps1:48:30:48:34 | code | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:48:30:48:34 | code | Possible injection path from user input to dangerous call to Invoke-Expression |
| test.ps1:75:25:75:54 | Get-Process -Name $UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:75:25:75:54 | Get-Process -Name $UserInput | Possible injection path from user input to dangerous call to Powershell |
| test.ps1:82:16:82:45 | Get-Process -Name $UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:82:16:82:45 | Get-Process -Name $UserInput | Possible injection path from user input to dangerous call to Powershell |
| test.ps1:89:12:89:28 | ping $UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:89:12:89:28 | ping $UserInput | Possible injection path from user input to dangerous call to Cmd |
| test.ps1:106:33:106:62 | Get-Process -Name $UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:106:33:106:62 | Get-Process -Name $UserInput | Possible injection path from user input to dangerous call to CreateScriptBlock |
| test.ps1:116:58:116:87 | Get-Process -Name $UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:116:58:116:87 | Get-Process -Name $UserInput | Possible injection path from user input to dangerous call to NewScriptBlock |
| test.ps1:124:34:124:43 | UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:124:34:124:43 | UserInput | Possible injection path from user input to dangerous call to ForEach-Object |
| test.ps1:131:28:131:37 | UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:131:28:131:37 | UserInput | Possible injection path from user input to dangerous call to Invoke |
| test.ps1:138:28:138:37 | UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:138:28:138:37 | UserInput | Possible injection path from user input to dangerous call to Invoke |
| test.ps1:168:50:168:59 | UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:168:50:168:59 | UserInput | Possible injection path from user input to dangerous call to ExpandString |
| test.ps1:176:63:176:72 | UserInput | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:176:63:176:72 | UserInput | Possible injection path from user input to dangerous call to ExpandString |
| test.ps1:192:23:192:54 | Get-Process -Name "$escaped" | test.ps1:195:10:195:32 | Call to Read-Host | test.ps1:192:23:192:54 | Get-Process -Name "$escaped" | Possible injection path from user input to dangerous call to Invoke-Expression |

View File

@@ -1 +0,0 @@
queries/security/cwe-078/InjectionHunter/UserInputToDangerousMethod.ql

View File

@@ -1,221 +0,0 @@
function Invoke-InvokeExpressionInjection1
{
param($UserInput)
Invoke-Expression "Get-Process -Name $UserInput"
}
function Invoke-InvokeExpressionInjection2
{
param($UserInput)
iex "Get-Process -Name $UserInput"
}
function Invoke-InvokeExpressionInjection3
{
param($UserInput)
$executionContext.InvokeCommand.InvokeScript("Get-Process -Name $UserInput")
}
function Invoke-InvokeExpressionInjection4
{
param($UserInput)
$host.Runspace.CreateNestedPipeline("Get-Process -Name $UserInput", $false).Invoke()
}
function Invoke-InvokeExpressionInjection5
{
param($UserInput)
[PowerShell]::Create().AddScript("Get-Process -Name $UserInput").Invoke()
}
function Invoke-InvokeExpressionInjection6
{
param($UserInput)
Add-Type "public class Foo { $UserInput }"
}
function Invoke-InvokeExpressionInjection7
{
param($UserInput)
Add-Type -TypeDefinition "public class Foo { $UserInput }"
}
function Invoke-InvokeExpressionInjection8
{
param($UserInput)
$code = "public class Foo { $UserInput }"
Add-Type -TypeDefinition $code
}
function Invoke-InvokeExpressionInjectionFP
{
param($UserInput)
$code = @"
public class BasicTest
{
public static int Add(int a, int b)
{
return (a + b);
}
public int Multiply(int a, int b)
{
return (a * b);
}
}
"@
Add-Type -TypeDefinition $code
}
function Invoke-ExploitableCommandInjection1
{
param($UserInput)
powershell -command "Get-Process -Name $UserInput"
}
function Invoke-ExploitableCommandInjection2
{
param($UserInput)
powershell "Get-Process -Name $UserInput"
}
function Invoke-ExploitableCommandInjection3
{
param($UserInput)
cmd /c "ping $UserInput"
}
#Allowed
function Invoke-ExploitableCommandInjectionFP
{
param($UserInput)
cmd /c "ping localhost"
}
function Invoke-ScriptBlockInjection1
{
param($UserInput)
## Often used when making remote connections
$sb = [ScriptBlock]::Create("Get-Process -Name $UserInput")
Invoke-Command RemoteServer $sb
}
function Invoke-ScriptBlockInjection2
{
param($UserInput)
## Often used when making remote connections
$sb = $executionContext.InvokeCommand.NewScriptBlock("Get-Process -Name $UserInput")
Invoke-Command RemoteServer $sb
}
function Invoke-MethodInjection1
{
param($UserInput)
Get-Process | Foreach-Object $UserInput
}
function Invoke-MethodInjection2
{
param($UserInput)
(Get-Process -Id $pid).$UserInput()
}
function Invoke-MethodInjection3
{
param($UserInput)
(Get-Process -Id $pid).$UserInput.Invoke()
}
#ALLOWED , uses script block
function Invoke-MethodInjectionFP1
{
param($UserInput)
Get-Process | Foreach-Object { $_.Name }
}
#ALLOWED, uses constant member access
function Invoke-MethodInjectionFP2
{
param($UserInput)
Get-Process | Foreach-Object "Name"
}
function Invoke-PropertyInjection
{
param($UserInput)
[DateTime]::$UserInput
}
function Invoke-ExpandStringInjection1
{
param($UserInput)
## Used to attempt a variable resolution
$executionContext.InvokeCommand.ExpandString($UserInput)
}
function Invoke-ExpandStringInjection2
{
param($UserInput)
## Used to attempt a variable resolution
$executionContext.SessionState.InvokeCommand.ExpandString($UserInput)
}
function Invoke-UnsafeEscape1
{
param($UserInput)
$escaped = $UserInput -replace "'", "''"
Invoke-Expression "Get-Process -Name '$escaped'"
}
function Invoke-UnsafeEscape2
{
param($UserInput)
$escaped = $UserInput -replace '"', '`"'
Invoke-Expression "Get-Process -Name `"$escaped`""
}
$input = Read-Host "enter input"
Invoke-InvokeExpressionInjection1 -UserInput $input
Invoke-InvokeExpressionInjection2 -UserInput $input
Invoke-InvokeExpressionInjection3 -UserInput $input
Invoke-InvokeExpressionInjection4 -UserInput $input
Invoke-InvokeExpressionInjection5 -UserInput $input
Invoke-InvokeExpressionInjection6 -UserInput $input
Invoke-InvokeExpressionInjection7 -UserInput $input
Invoke-InvokeExpressionInjection8 -UserInput $input
Invoke-InvokeExpressionInjectionFP -UserInput $input
Invoke-ExploitableCommandInjection1 -UserInput $input
Invoke-ExploitableCommandInjection2 -UserInput $input
Invoke-ExploitableCommandInjection3 -UserInput $input
Invoke-ExploitableCommandInjectionFP -UserInput $input
Invoke-ScriptBlockInjection1 -UserInput $input
Invoke-ScriptBlockInjection2 -UserInput $input
Invoke-MethodInjection1 -UserInput $input
Invoke-MethodInjection2 -UserInput $input
Invoke-MethodInjection3 -UserInput $input
Invoke-MethodInjectionFP1 -UserInput $input
Invoke-MethodInjectionFP2 -UserInput $input
Invoke-PropertyInjection -UserInput $input
Invoke-ExpandStringInjection1 -UserInput $input
Invoke-ExpandStringInjection2 -UserInput $input
Invoke-UnsafeEscape1 -UserInput $input
Invoke-UnsafeEscape2 -UserInput $input