+
+Code that passes user input directly to
+Invoke-Expression, &, or some other library
+routine that executes a command, allows the user to execute malicious
+code.
+
+
+
+
+Possible script injection risk via the Invoke-Expression cmdlet. 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.
+
+
+
+
+The following example shows code that takes a shell script that can be changed
+maliciously by a user, and passes it straight to Invoke-Expression
+without examining it first.
+
+
+
+
+
+
+
+OWASP:
+Command Injection.
+
+
+
+
+
+
diff --git a/powershell/ql/src/experimental/InjectionHunter/UserInputToDangerousMethod.ql b/powershell/ql/src/experimental/InjectionHunter/UserInputToDangerousMethod.ql
new file mode 100644
index 00000000000..bdbcdbddfe3
--- /dev/null
+++ b/powershell/ql/src/experimental/InjectionHunter/UserInputToDangerousMethod.ql
@@ -0,0 +1,172 @@
+/**
+ * @name User Input to Invoke-Expression
+ * @description Finding cases where the user input is passed an Invoke-Expression command
+ * @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
+
+private module TestConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node source) {
+ exists(CmdCall c |
+ c.getName() = "Read-Host" and
+ source.asExpr().getExpr() = c) }
+
+ 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(InvokeMemberExpr ie |
+ this.asExpr().getExpr() = ie.getAnArgument() and
+ ie.getName() = "InvokeScript" and
+ ie.getQualifier().toString() = "InvokeCommand" and
+ ie.getQualifier().getAChild().toString() = "executioncontext"
+ )
+ }
+}
+
+class CreateNestedPipelineSink extends Sink {
+ CreateNestedPipelineSink() {
+ exists(InvokeMemberExpr ie |
+ this.asExpr().getExpr() = ie.getAnArgument() and
+ ie.getName() = "CreateNestedPipeline" and
+ ie.getQualifier().toString() = "InvokeCommand" and
+ ie.getQualifier().getAChild().toString() = "executioncontext")
+ }
+}
+
+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"
+ )
+ }
+}
+
+abstract class Sanitizer extends DataFlow::Node {}
+
+// class TypedParameterSanitizer extends Sanitizer{
+// TypedParameterSanitizer() {
+// exists(Function f, CmdCall c, Parameter p, Argument a |
+// p = f.getAParameter() and
+// a = c.getAnArgument() and
+// p.getName().toLowerCase() = a.getName() and
+// p.getStaticType() != "Object" and
+// c.getName() = f.getName() and
+
+// this.asExpr().getExpr() = a
+// )
+// }
+// }
+
+class SingleQuoteSanitizer extends Sanitizer {
+ SingleQuoteSanitizer() {
+ exists(Expr e, VarReadAccess v |
+ e = this.asExpr().getExpr().getParent() and
+ e.toString().matches("%'$" + v.getVariable().getName() + "'%")
+ )
+ }
+}
+
+module TestFlow = TaintTracking::Global