Esempio n. 1
0
        public static void MeasureInvokeExpression(string ScriptBlock)
        {
            Token[]        token;
            ParseError[]   error;
            ScriptBlockAst ScriptBlockAst = Parser.ParseInput(ScriptBlock, out token, out error);

            Func <Ast, bool> predicate = delegate(Ast ast)
            {
                var        derpVar   = new VariableExpressionAst(ast.Extent, "Derp", splatted: false);
                CommandAst targetAst = new CommandAst(ast.Extent, new[] { derpVar }, TokenKind.Unknown, Enumerable.Empty <RedirectionAst>());
                if (targetAst != null)
                {
                    if (targetAst.CommandElements[0].Extent.Text.In(false, new List <string> {
                        "iex", "invoke-expression"
                    }))
                    {
                        return(true);
                    }
                }
                return(false);
            };

            var foundNode = ScriptBlockAst.Find(predicate, true);

            if (foundNode != null)
            {
                Console.WriteLine("[+] Possible injection vulnerability found");
                Console.WriteLine(String.Format(@"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.
RuleName = InjectionRisk.InvokeExpression
Severity = Warning", foundNode.Extent
                                                ));
            }
        }
Esempio n. 2
0
        public static void MeasureMethodInjection(string ScriptBlock)
        {
            Token[]        token;
            ParseError[]   error;
            ScriptBlockAst ScriptBlockAst = Parser.ParseInput(ScriptBlock, out token, out error);

            Func <Ast, bool> predicate = delegate(Ast ast)
            {
                var derpVar  = new VariableExpressionAst(ast.Extent, "Derp", splatted: false);
                var derpVar2 = new StringConstantExpressionAst(ast.Extent, "Derpvr2", StringConstantType.BareWord);
                InvokeMemberExpressionAst targetAst = new InvokeMemberExpressionAst(ast.Extent, derpVar, derpVar2, Enumerable.Empty <ExpressionAst>(), @static: false);
                if (targetAst != null)
                {
                    if (targetAst.Member is ConstantExpressionAst)
                    {
                        return(true);
                    }
                }
                return(false);
            };

            var foundNode = ScriptBlockAst.Find(predicate, true);

            if (foundNode != null)
            {
                Console.WriteLine("[+] Possible injection vulnerability found");
                Console.WriteLine(String.Format(@"Possible property access injection via dynamic member access. Untrusted input can cause arbitrary static properties to be accessed:
RuleName = InjectionRisk.MethodInjection
Severity = Warning", foundNode.Extent));
            }
        }
Esempio n. 3
0
        public static void MeasureCommandInjection(string ScriptBlock)
        {
            Token[]        token;
            ParseError[]   error;
            ScriptBlockAst ScriptBlockAst = Parser.ParseInput(ScriptBlock, out token, out error);

            Func <Ast, bool> predicate = delegate(Ast ast)
            {
                var        derpVar   = new VariableExpressionAst(ast.Extent, "Derp", splatted: false);
                CommandAst targetAst = new CommandAst(ast.Extent, new[] { derpVar }, TokenKind.Unknown, Enumerable.Empty <RedirectionAst>());
                if (targetAst != null)
                {
                    if (targetAst.CommandElements[0].Extent.Text.In(false, new List <string> {
                        "cmd", "powershell"
                    }))
                    {
                        var commandInvoked = targetAst.CommandElements[1];
                        for (int parameterPosition = 1; parameterPosition < targetAst.CommandElements.Count; parameterPosition++)
                        {
                            if (targetAst.CommandElements[parameterPosition].Extent.Text.In(false, new List <string> {
                                "/c", "/k", "command", "-c", "-enc"
                            }))
                            {
                                commandInvoked = targetAst.CommandElements[parameterPosition + 1];
                                break;
                            }
                        }
                        if (commandInvoked is ExpandableStringExpressionAst)
                        {
                            return(true);
                        }
                    }
                }
                return(false);
            };

            var foundNode = ScriptBlockAst.Find(predicate, true);

            if (foundNode != null)
            {
                Console.WriteLine("[+] Possible injection vulnerability found");
                Console.WriteLine(String.Format(@"Possible command injection risk via calling cmd.exe or powershell.exe. Untrusted input can cause arbitrary commands to be run.
Input should be provided as variable input directly (such as 'cmd /c ping $destination', rather than within an expandable string.
The PowerShell.AddCommand().AddParameter() APIs should be used instead.
RuleName = InjectionRisk.CommandInjection
Severity = Warning", foundNode.Extent));
            }
        }
Esempio n. 4
0
        public static void MeasureForeachObjectInjection(string ScriptBlock)
        {
            Token[]        token;
            ParseError[]   error;
            ScriptBlockAst ScriptBlockAst = Parser.ParseInput(ScriptBlock, out token, out error);

            Func <Ast, bool> predicate = delegate(Ast ast)
            {
                var        derpVar   = new VariableExpressionAst(ast.Extent, "Derp", splatted: false);
                CommandAst targetAst = new CommandAst(ast.Extent, new[] { derpVar }, TokenKind.Unknown, Enumerable.Empty <RedirectionAst>());
                if (targetAst != null)
                {
                    if (targetAst.CommandElements[0].Extent.Text.In(false, new List <string> {
                        "foreach", "%"
                    }))
                    {
                        var memberInvoked = targetAst.CommandElements[1];
                        for (int parameterPosition = 1; parameterPosition < targetAst.CommandElements.Count; parameterPosition++)
                        {
                            if (targetAst.CommandElements[parameterPosition].Extent.Text.In(false, new List <string> {
                                "process", "membername"
                            }))
                            {
                                memberInvoked = targetAst.CommandElements[parameterPosition + 1];
                                break;
                            }
                        }
                        if (memberInvoked is ConstantExpressionAst && memberInvoked is ScriptBlockExpressionAst)
                        {
                            return(true);
                        }
                    }
                }
                return(false);
            };
            var foundNode = ScriptBlockAst.Find(predicate, true);

            if (foundNode != null)
            {
                Console.WriteLine("[+] Possible injection vulnerability found");
                Console.WriteLine(String.Format(@"Possible property access injection via Foreach-Object. Untrusted input can cause arbitrary properties /methods to be accessed:
RuleName = InjectionRisk.ForeachObjectInjection
Severity = Warning", foundNode.Extent));
            }
        }
Esempio n. 5
0
        //public static void MeasureAddType(string ScriptBlock)
        //{
        //    Token[] token;
        //    ParseError[] error;
        //    ScriptBlockAst ScriptBlockAst = Parser.ParseInput(ScriptBlock, out token, out error);
        //
        //    Func<Ast, bool> predicate = delegate (Ast ast)
        //    {
        //        var derpVar = new VariableExpressionAst(ast.Extent, "Derp", splatted: false);
        //        CommandAst targetAst = new CommandAst(ast.Extent, new[] { derpVar }, TokenKind.Unknown, Enumerable.Empty<RedirectionAst>());
        //        if (targetAst.CommandElements[0].Extent.Text == "Add-Type")
        //        {
        //            var addTypeParameters = StaticParameterBinder.BindCommand(targetAst);
        //            var typeDefinitionParameter = addTypeParameters.BoundParameters.TypeDefinition;
        //            if (typeDefinitionParameter.ConstantValue)
        //            {
        //                if (addTypeParameters.BoundParameters.TypeDefinition.ValueSystem.Management.Automation.Language.VariableExpressionAst)
        //                {
        //                    var variableName = addTypeParameters.BoundParameters.TypeDefinition.Value.VariablePath.UserPath;
        //                    var constantAssignmentForVariable = ScriptBlockAst.FindAll(tempvar => tempvar is Ast, true);
        //                    if (assignmentAst && assignmentAst.Left.VariablePath.UserPath == variableName && assignmentAst.Right.ExpressionSystem.Management.Automation.Language.ConstantExpressionAst)
        //                    {
        //                        return true;
        //                    }
        //                    if (constantAssignmentForVariable != null)
        //                    {
        //                        return false;
        //                    }
        //                    else
        //                    {
        //                        return true;
        //                    }
        //                }
        //                return true;
        //            }
        //        }
        //        return false;
        //    };
        //
        //    var foundNode = ScriptBlockAst.Find(predicate, true);
        //    if (foundNode != null)
        //    {
        //        Console.WriteLine("[+] Possible injection vulnerability found");
        //        Console.WriteLine(String.Format(@"Possible code injection risk via the Add-Type cmdlet. Untrusted input can cause arbitrary Win32 code to be run..
//RuleName = InjectionRisk.AddType
//Severity = Warning", foundNode.Extent
        //        ));
        //    }
        //}

        public static void MeasureDangerousMethod(string ScriptBlock)
        {
            Token[]        token;
            ParseError[]   error;
            ScriptBlockAst ScriptBlockAst = Parser.ParseInput(ScriptBlock, out token, out error);

            Func <Ast, bool> predicate = delegate(Ast ast)
            {
                var derpVar  = new VariableExpressionAst(ast.Extent, "Derp", splatted: false);
                var derpVar2 = new StringConstantExpressionAst(ast.Extent, "derbvar2", StringConstantType.BareWord);
                InvokeMemberExpressionAst targetAst = new InvokeMemberExpressionAst(ast.Extent, derpVar, derpVar2, Enumerable.Empty <ExpressionAst>(), false);
                if (targetAst != null)
                {
                    if (targetAst.Member.Extent.Text.In(false, new List <string> {
                        "invokescript", "createnestedpipeline", "addscript", "newscriptblock", "expandstring"
                    }))
                    {
                        return(true);
                    }
                    if (targetAst.Member.Extent.Text.In(false, new List <string> {
                        "create"
                    }) && targetAst.Expression.Extent.Text.In(false, new List <string> {
                        "scriptblock"
                    }))
                    {
                        return(true);
                    }
                }
                return(false);
            };

            var foundNode = ScriptBlockAst.Find(predicate, true);

            if (foundNode != null)
            {
                Console.WriteLine("[+] Possible injection vulnerability found");
                Console.WriteLine(String.Format(@"Possible script injection risk via the a dangerous method. Untrusted input can cause arbitrary PowerShell expressions to be run.
The PowerShell.AddCommand().AddParameter() APIs should be used instead.
RuleName = {1}
Severity = Warning", foundNode.Extent, foundNode.Extent.Text)
                                  );
            }
        }
Esempio n. 6
0
        public static void MeasureUnsafeEscaping(string ScriptBlock)
        {
            Token[]        token;
            ParseError[]   error;
            ScriptBlockAst ScriptBlockAst = Parser.ParseInput(ScriptBlock, out token, out error);

            Func <Ast, bool> predicate = delegate(Ast ast)
            {
                var leftVariable  = new VariableExpressionAst(ast.Extent, "herp", splatted: false);
                var rightVariable = new VariableExpressionAst(ast.Extent, "derp", splatted: false);
                var targetAst     = new BinaryExpressionAst(ast.Extent, leftVariable, TokenKind.Ieq, rightVariable, ast.Extent);
                if (targetAst != null)
                {
                    if (targetAst.Operator.In(false, new List <string> {
                        "replace"
                    }) && targetAst.Right.Extent.Text.In(false, new List <string> {
                        "`", "'"
                    }))
                    {
                        return(true);
                    }
                }
                return(false);
            };

            var foundNode = ScriptBlockAst.Find(predicate, true);

            if (foundNode != null)
            {
                Console.WriteLine("[+] Possible injection vulnerability found");
                Console.WriteLine(@"Possible unsafe use of input escaping. 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 the  [System.Management.Automation.Language.CodeGeneration]::Escape* should be used instead
RuleName = InjectionRisk.UnsafeEscaping
Severity = Warning");
            }
        }
        /// <summary>
        /// Get the module name from the error extent
        ///
        /// If a parser encounters Import-DSCResource -ModuleName SomeModule
        /// and if SomeModule is not present in any of the PSModulePaths, the
        /// parser throws ModuleNotFoundDuringParse Error. We correlate the
        /// error message with extent to extract the module name as the error
        /// record doesn't provide direct access to the missing module name.
        /// </summary>
        /// <param name="error">Parse error</param>
        /// <param name="ast">AST of the script that contians the parse error</param>
        /// <param name="moduleVersion">Specifc version of the required module</param>
        /// <returns>The name of the module that caused the parser to throw the error. Returns null if it cannot extract the module name.</returns>
        public static IEnumerable <string> GetModuleNameFromErrorExtent(ParseError error, ScriptBlockAst ast, out Version moduleVersion)
        {
            moduleVersion = null;
            ThrowIfNull(error, "error");
            ThrowIfNull(ast, "ast");
            var statement      = ast.Find(x => x.Extent.Equals(error.Extent), true);
            var dynamicKywdAst = statement as DynamicKeywordStatementAst;

            if (dynamicKywdAst == null)
            {
                return(null);
            }
            // check if the command name is import-dscmodule
            // right now we handle only the following forms
            // 1. Import-DSCResourceModule -ModuleName somemodule
            // 2. Import-DSCResourceModule -ModuleName somemodule1 -ModuleVersion major.minor.patch.build
            // 3. Import-DSCResourceModule -ModuleName somemodule1,somemodule2
            var dscKeywordAst = dynamicKywdAst.CommandElements[0] as StringConstantExpressionAst;

            if (dscKeywordAst == null || !dscKeywordAst.Value.Equals("Import-DscResource", StringComparison.OrdinalIgnoreCase))
            {
                return(null);
            }

            // find a parameter named modulename
            int positionOfModuleNameParamter     = 0;
            int positionOfModuleVersionParameter = 0;

            for (int i = 1; i < dynamicKywdAst.CommandElements.Count; i++)
            {
                var paramAst = dynamicKywdAst.CommandElements[i] as CommandParameterAst;
                // TODO match the initial letters only
                if (paramAst != null && paramAst.ParameterName.Equals("ModuleName", StringComparison.OrdinalIgnoreCase))
                {
                    if (i == dynamicKywdAst.CommandElements.Count)
                    {
                        // command was Save-DscDependency ... -ModuleName -> module name missing
                        return(null);
                    }
                    positionOfModuleNameParamter = i + 1;
                    continue;
                }

                if (paramAst != null && paramAst.ParameterName.Equals("ModuleVersion", StringComparison.OrdinalIgnoreCase))
                {
                    if (i == dynamicKywdAst.CommandElements.Count)
                    {
                        // command was Save-DscDependency ... -ModuleVersion -> module version missing
                        return(null);
                    }
                    positionOfModuleVersionParameter = i + 1;
                    continue;
                }
            }

            var modules = new List <string>();

            var paramValAst = dynamicKywdAst.CommandElements[positionOfModuleNameParamter];

            // import-dscresource -ModuleName module1
            if (paramValAst is StringConstantExpressionAst paramValStrConstExprAst)
            {
                modules.Add(paramValStrConstExprAst.Value);

                // import-dscresource -ModuleName module1 -ModuleVersion major.minor.patch.build
                var versionParameterAst = dynamicKywdAst.CommandElements[positionOfModuleVersionParameter] as StringConstantExpressionAst;
                if (versionParameterAst != null)
                {
                    Version.TryParse(versionParameterAst.Value, out moduleVersion); // ignore return value since a module version of null means no version
                }
                return(modules);
            }

            // Import-DscResource –ModuleName @{ModuleName="module1";ModuleVersion="1.2.3.4"}
            //var paramValAstHashtableAst = paramValAst.Find(oneAst => oneAst is HashtableAst, true) as HashtableAst;
            if (paramValAst.Find(oneAst => oneAst is HashtableAst, true) is HashtableAst paramValAstHashtableAst)
            {
                var moduleNameTuple = paramValAstHashtableAst.KeyValuePairs.SingleOrDefault(x => x.Item1.Extent.Text.Equals("ModuleName"));
                var moduleName      = moduleNameTuple.Item2.Find(astt => astt is StringConstantExpressionAst, true) as StringConstantExpressionAst;
                if (moduleName == null)
                {
                    return(null);
                }
                modules.Add(moduleName.Value);
                var moduleVersionTuple = paramValAstHashtableAst.KeyValuePairs.SingleOrDefault(x => x.Item1.Extent.Text.Equals("ModuleVersion"));
                var moduleVersionAst   = moduleVersionTuple.Item2.Find(astt => astt is StringConstantExpressionAst, true) as StringConstantExpressionAst;
                Version.TryParse(moduleVersionAst.Value, out moduleVersion);
                return(modules);
            }

            // import-dscresource -ModuleName module1,module2
            if (paramValAst is ArrayLiteralAst paramValArrLtrlAst)
            {
                foreach (var elem in paramValArrLtrlAst.Elements)
                {
                    var elemStrConstExprAst = elem as StringConstantExpressionAst;
                    if (elemStrConstExprAst != null)
                    {
                        modules.Add(elemStrConstExprAst.Value);
                    }
                }
                if (modules.Count == 0)
                {
                    return(null);
                }
                return(modules);
            }

            return(null);
        }
        /// <summary>
        /// Get the module name from the error extent
        ///
        /// If a parser encounters Import-DSCResource -ModuleName SomeModule
        /// and if SomeModule is not present in any of the PSModulePaths, the
        /// parser throws ModuleNotFoundDuringParse Error. We correlate the
        /// error message with extent to extract the module name as the error
        /// record doesn't provide direct access to the missing module name.
        /// </summary>
        /// <param name="error">Parse error</param>
        /// <param name="ast">AST of the script that contians the parse error</param>
        /// <returns>The name of the module that caused the parser to throw the error. Returns null if it cannot extract the module name.</returns>
        public static IEnumerable <string> GetModuleNameFromErrorExtent(ParseError error, ScriptBlockAst ast)
        {
            ThrowIfNull(error, "error");
            ThrowIfNull(ast, "ast");
            var statement      = ast.Find(x => x.Extent.Equals(error.Extent), true);
            var dynamicKywdAst = statement as DynamicKeywordStatementAst;

            if (dynamicKywdAst == null)
            {
                return(null);
            }
            // check if the command name is import-dscmodule
            // right now we handle only the following forms
            // 1. Import-DSCResourceModule -ModuleName somemodule
            // 2. Import-DSCResourceModule -ModuleName somemodule1,somemodule2
            if (dynamicKywdAst.CommandElements.Count < 3)
            {
                return(null);
            }

            var dscKeywordAst = dynamicKywdAst.CommandElements[0] as StringConstantExpressionAst;

            if (dscKeywordAst == null || !dscKeywordAst.Value.Equals("Import-DscResource", StringComparison.OrdinalIgnoreCase))
            {
                return(null);
            }

            // find a parameter named modulename
            int k;

            for (k = 1; k < dynamicKywdAst.CommandElements.Count; k++)
            {
                var paramAst = dynamicKywdAst.CommandElements[1] as CommandParameterAst;
                // TODO match the initial letters only
                if (paramAst == null || !paramAst.ParameterName.Equals("ModuleName", StringComparison.OrdinalIgnoreCase))
                {
                    continue;
                }
                break;
            }

            if (k == dynamicKywdAst.CommandElements.Count)
            {
                // cannot find  modulename
                return(null);
            }
            var modules = new List <string>();

            // k < count - 1, because only -ModuleName throws parse error and hence not possible
            var paramValAst = dynamicKywdAst.CommandElements[++k];

            // import-dscresource -ModuleName module1
            var paramValStrConstExprAst = paramValAst as StringConstantExpressionAst;

            if (paramValStrConstExprAst != null)
            {
                modules.Add(paramValStrConstExprAst.Value);
                return(modules);
            }

            // import-dscresource -ModuleName module1,module2
            var paramValArrLtrlAst = paramValAst as ArrayLiteralAst;

            if (paramValArrLtrlAst != null)
            {
                foreach (var elem in paramValArrLtrlAst.Elements)
                {
                    var elemStrConstExprAst = elem as StringConstantExpressionAst;
                    if (elemStrConstExprAst != null)
                    {
                        modules.Add(elemStrConstExprAst.Value);
                    }
                }
                if (modules.Count == 0)
                {
                    return(null);
                }
                return(modules);
            }
            return(null);
        }