moved folder, added tests/docs

This commit is contained in:
Chanel Young
2025-04-16 12:13:07 -07:00
parent 50a771edee
commit 2266cd2eb8
8 changed files with 576 additions and 253 deletions

View File

@@ -1,241 +0,0 @@
/**
* @name User Input to Invoke-Expression
* @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-invoke-expression
* @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
private module TestConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source instanceof SourceNode or
source instanceof Source
}
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) {node instanceof Sanitizer}
}
abstract class Source extends DataFlow::Node {}
class ReadHostSource extends Source {
ReadHostSource() {
exists(CmdCall c |
this.asExpr().getExpr() = c and
c.getName() = "Read-Host" )
}
}
class GetContentSource extends Source {
GetContentSource() {
exists(CmdCall c |
this.asExpr().getExpr() = c and
c.getName() = "Get-Content" )
}
}
class ValueFromPipelineSource extends Source {
ValueFromPipelineSource() {
exists(Parameter p |
p.getAnAttribute().toString() = "ValueFromPipeline" and
this.asExpr().getExpr() = p.getAnAccess()
)
}
}
abstract class Sink extends DataFlow::Node {}
class InvokeExpressionCall extends Sink {
InvokeExpressionCall() {
exists(CmdCall c |
this.asExpr().getExpr() = c.getAnArgument() and
c.getName() = ["Invoke-Expression", "iex", "Add-Type" ] )
}
}
class InvokeScriptSink extends Sink {
InvokeScriptSink() {
exists(API::Node call |
API::getTopLevelMember("executioncontext").getMember("invokecommand").getMethod("invokescript") = call and
this = call.getArgument(_).asSink()
)
}
}
class CreateNestedPipelineSink extends Sink {
CreateNestedPipelineSink() {
exists(API::Node call |
API::getTopLevelMember("host").getMember("runspace").getMethod("createnestedpipeline") = call and
this = call.getArgument(_).asSink()
)
}
}
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"
)
}
}
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)
)
)
}
}
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"
)
}
}
class ForEachObjectSink extends Sink {
ForEachObjectSink() {
exists(CmdCall c |
this.asExpr().getExpr() = c.getAnArgument() and
c.getName() = "Foreach-Object"
)
}
}
class InvokeSink extends Sink {
InvokeSink() {
exists(InvokeMemberExpr ie |
this.asExpr().getExpr() = ie.getCallee() or
this.asExpr().getExpr() = ie.getQualifier().getAChild*()
)
}
}
class CreateScriptBlockSink extends Sink {
CreateScriptBlockSink() {
exists(InvokeMemberExpr ie |
this.asExpr().getExpr() = ie.getAnArgument() and
ie.getName() = "Create" and
ie.getQualifier().toString() = "ScriptBlock"
)
}
}
class NewScriptBlockSink extends Sink {
NewScriptBlockSink() {
exists(API::Node call |
API::getTopLevelMember("executioncontext").getMember("invokecommand").getMethod("newscriptblock") = call and
this = call.getArgument(_).asSink()
)
}
}
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
)
}
}
module TestFlow = TaintTracking::Global<TestConfig>;
import TestFlow::PathGraph
from TestFlow::PathNode source, TestFlow::PathNode sink
where
TestFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Flow from user input to dangerous method"
// from CmdCall c
// where c.getName() = "cmd"
// and c.getArgument(0).getValue().toString() = "/c"
// select c.getArgument(1)
// from InvokeMemberExpr ie
// where ie.getName() = "Create" and
// ie.getQualifier().toString() = "ScriptBlock"
// select ie, ie.getQualifier(), ie.getAnArgument()
// from API::Node call
// where API::getTopLevelMember("executioncontext").getMember("invokecommand").getMethod("newscriptblock") = call
// select call, call.getArgument(_).asSink()
// from Expr e
// where e.getLocation().getFile().getBaseName() = "InjectionHunterTests.ps1"
// and e.getLocation().getStartLine() = 106
// select e, e.getAQlClass()
// from Function f, CmdCall c
// where f.getLocation().getFile().getBaseName() = "sanitizers.ps1"
// select f, f.getAParameter().getStaticType(), f.getAParameter().getName()
//TBD, waiting on mathias on how to connect f and c
// from Function f, CmdCall c, Parameter p, Argument a
// where
// p = f.getAParameter() and
// a = c.getAnArgument() and
// p.getName().toLowerCase() = a.getName() and
// p.getStaticType() != "Object" and
// c.getName() = f.getName()
// select a, "argument has a specified static type"
// from Argument a, VarReadAccess v
// where a.getAChild() = v and
// v.getVariable().getName() = "UserInput"
// select a, v
// from Argument e
// where e.getLocation().getFile().getBaseName() = "sanitizers.ps1"
// and e.getLocation().getStartLine() = 14
// select e, e.getAChild(), e.getParent(), e.toString()
// from PipelineParameter p
// where p.getLocation().getFile().getBaseName() = "userinput.ps1"
// select p, p.getName(), p.getAChild()
// from Attribute a
// select a, a.getParent(), a.getParent().getAQlClass(), a.getANamedArgument()
// from Expr e
// where e.getLocation().getFile().getBaseName() = "sanitizers.ps1"
// and e.getLocation().getStartLine() = 31
// select e, e.getAQlClass()
// from InvokeMemberExpr ie
// where
// ie.getLocation().getStartLine() = 28 and ie.getName() = "AddScript"
// select ie, ie.getName(), ie.getQualifier().toString(), ie.getQualifier().getAChild().toString(), ie.getParent().(InvokeMemberExpr).getName()

View File

@@ -0,0 +1,152 @@
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

@@ -8,33 +8,41 @@
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 via the Invoke-Expression cmdlet. Untrusted input can cause arbitrary PowerShell expressions to be run.
<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>
<example>
<p>The following example shows code that takes a shell script that can be changed
maliciously by a user, and passes it straight to <code>Invoke-Expression</code>
without examining it first.</p>
<sample src="examples/command_injection.ps1" />
</example>
<references>
<li>
OWASP:
<a href="https://www.owasp.org/index.php/Command_Injection">Command Injection</a>.
</li>
<!-- LocalWords: CWE untrusted unsanitized Runtime
-->
<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

@@ -0,0 +1,36 @@
/**
* @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

@@ -0,0 +1,146 @@
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

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

View File

@@ -0,0 +1,221 @@
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