From a75ed069f44c7a38a8f85ff1d39ed32cea87115e Mon Sep 17 00:00:00 2001 From: sky5454 Date: Sun, 10 May 2026 02:12:14 +0800 Subject: [PATCH] fix(powershell): sec deny powershell encoding bypass via iex injection. --- pkg/tools/shell.go | 8 ++++++++ pkg/tools/shell_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/pkg/tools/shell.go b/pkg/tools/shell.go index a570ac9ec..61b797393 100644 --- a/pkg/tools/shell.go +++ b/pkg/tools/shell.go @@ -95,6 +95,14 @@ var ( regexp.MustCompile(`\bssh\b.*@`), regexp.MustCompile(`\beval\b`), regexp.MustCompile(`\bsource\s+.*\.sh\b`), + // PowerShell encoding bypass: [Text.Encoding] used to construct command strings. + regexp.MustCompile(`\[text\.encoding\]`), + // PowerShell -EncodedCommand flag (base64-encoded command). + regexp.MustCompile(`-encodedcommand`), + // .GetString called on byte array to decode commands. + regexp.MustCompile(`\.getstring\(\[byte\[\]`), + // FromBase64String used in command construction chain. + regexp.MustCompile(`frombase64string\(`), } // absolutePathPattern matches absolute file paths in commands (Unix and Windows). diff --git a/pkg/tools/shell_test.go b/pkg/tools/shell_test.go index a8de2f4c9..64e8d6534 100644 --- a/pkg/tools/shell_test.go +++ b/pkg/tools/shell_test.go @@ -703,6 +703,44 @@ func TestShellTool_URLBypassPrevented(t *testing.T) { } } +// TestShellTool_PowerShellEncodingBypass verifies that PowerShell encoding bypass techniques are blocked. +func TestShellTool_PowerShellEncodingBypass(t *testing.T) { + tool, err := NewExecTool("", false) + require.NoError(t, err) + + ctx := context.Background() + + // Commands using [Text.Encoding] to construct a command string at runtime. + encodingBypassCommands := []string{ + `[Text.Encoding]::ASCII.GetString([byte[]](0x6c,0x73,0x20,0x7e))`, + `[Text.Encoding]::ASCII.GetString([byte[]](0x69,0x65,0x78))`, + `[System.Text.Encoding]::ASCII.GetString([byte[]](0x69,0x65,0x78))`, + } + + for _, cmd := range encodingBypassCommands { + result := tool.Execute(ctx, map[string]any{"action": "run", "command": cmd}) + if !result.IsError { + t.Errorf("expected [Text.Encoding] bypass to be blocked: %s", cmd) + } + if !strings.Contains(result.ForLLM, "blocked") && !strings.Contains(result.ForUser, "blocked") { + t.Errorf("expected 'blocked' message for %s, got: %s", cmd, result.ForLLM) + } + } + + // Commands using PowerShell's -EncodedCommand flag (base64). + encodedCommands := []string{ + `powershell -NoProfile -NonInteractive -EncodedCommand SQBFAHIAaABlAGwAbAAvAC8A`, + `pwsh -EncodedCommand aWV4`, + } + + for _, cmd := range encodedCommands { + result := tool.Execute(ctx, map[string]any{"action": "run", "command": cmd}) + if !result.IsError { + t.Errorf("expected -EncodedCommand to be blocked: %s", cmd) + } + } +} + func TestShellTool_Background_ReturnsImmediately(t *testing.T) { tool, err := NewExecTool("", false) require.NoError(t, err)