diff options
author | Bryan McNulty <bryanmcnulty@protonmail.com> | 2025-03-07 08:52:48 -0600 |
---|---|---|
committer | Bryan McNulty <bryanmcnulty@protonmail.com> | 2025-03-07 08:52:48 -0600 |
commit | e87dd341dde93c289b6774f636e6767476b84a79 (patch) | |
tree | 3181b18f79b587bd04d98ed886f3505f37faeb2d /cmd | |
parent | a5c860b8ab24c198b7390fbde90044754e35c1c5 (diff) | |
download | goexec-e87dd341dde93c289b6774f636e6767476b84a79.tar.gz goexec-e87dd341dde93c289b6774f636e6767476b84a79.zip |
Added wmiexec module + updated TODO
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/root.go | 122 | ||||
-rw-r--r-- | cmd/wmi.go | 142 |
2 files changed, 205 insertions, 59 deletions
diff --git a/cmd/root.go b/cmd/root.go index 473f1ad..9a84e28 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,79 +1,83 @@ package cmd import ( - "context" - "fmt" - "github.com/RedTeamPentesting/adauth" - "github.com/rs/zerolog" - "github.com/spf13/cobra" - "os" - "regexp" + "context" + "fmt" + "github.com/RedTeamPentesting/adauth" + "github.com/rs/zerolog" + "github.com/spf13/cobra" + "os" + "regexp" ) var ( - //logFile string - log zerolog.Logger - ctx context.Context - authOpts *adauth.Options + //logFile string + log zerolog.Logger + ctx context.Context + authOpts *adauth.Options - debug bool - command string - executable string - executablePath string - executableArgs string + debug bool + command string + executable string + executablePath string + executableArgs string + workingDirectory string - needsTarget = func(cmd *cobra.Command, args []string) (err error) { - if len(args) != 1 { - return fmt.Errorf("command require exactly one positional argument: [target]") - } - if creds, target, err = authOpts.WithTarget(ctx, "cifs", args[0]); err != nil { - return fmt.Errorf("failed to parse target: %w", err) - } - if creds == nil { - return fmt.Errorf("no credentials supplied") - } - if target == nil { - return fmt.Errorf("no target supplied") - } - return - } + needsTarget = func(cmd *cobra.Command, args []string) (err error) { + if len(args) != 1 { + return fmt.Errorf("command require exactly one positional argument: [target]") + } + if creds, target, err = authOpts.WithTarget(ctx, "cifs", args[0]); err != nil { + return fmt.Errorf("failed to parse target: %w", err) + } + if creds == nil { + return fmt.Errorf("no credentials supplied") + } + if target == nil { + return fmt.Errorf("no target supplied") + } + return + } - rootCmd = &cobra.Command{ - Use: "goexec", - PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) { - // For modules that require a full executable path - if executablePath != "" && !regexp.MustCompile(`^([a-zA-Z]:)?\\`).MatchString(executablePath) { - return fmt.Errorf("executable path (-e) must be an absolute Windows path, i.e. C:\\Windows\\System32\\cmd.exe") - } - log = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).Level(zerolog.InfoLevel).With().Timestamp().Logger() - if debug { - log = log.Level(zerolog.DebugLevel) - } - return - }, - } + rootCmd = &cobra.Command{ + Use: "goexec", + PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) { + // For modules that require a full executable path + if executablePath != "" && !regexp.MustCompile(`^([a-zA-Z]:)?\\`).MatchString(executablePath) { + return fmt.Errorf("executable path (-e) must be an absolute Windows path, i.e. C:\\Windows\\System32\\cmd.exe") + } + log = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).Level(zerolog.InfoLevel).With().Timestamp().Logger() + if debug { + log = log.Level(zerolog.DebugLevel) + } + return + }, + } ) func init() { - ctx = context.Background() + ctx = context.Background() - rootCmd.InitDefaultVersionFlag() - rootCmd.InitDefaultHelpCmd() - rootCmd.PersistentFlags().BoolVar(&debug, "debug", false, "Enable debug logging") + rootCmd.InitDefaultVersionFlag() + rootCmd.InitDefaultHelpCmd() + rootCmd.PersistentFlags().BoolVar(&debug, "debug", false, "Enable debug logging") - authOpts = &adauth.Options{Debug: log.Debug().Msgf} - authOpts.RegisterFlags(rootCmd.PersistentFlags()) + authOpts = &adauth.Options{Debug: log.Debug().Msgf} + authOpts.RegisterFlags(rootCmd.PersistentFlags()) - scmrCmdInit() - rootCmd.AddCommand(scmrCmd) + scmrCmdInit() + rootCmd.AddCommand(scmrCmd) - tschCmdInit() - rootCmd.AddCommand(tschCmd) + tschCmdInit() + rootCmd.AddCommand(tschCmd) + + wmiCmdInit() + rootCmd.AddCommand(wmiCmd) } func Execute() { - if err := rootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(1) - } + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } } diff --git a/cmd/wmi.go b/cmd/wmi.go new file mode 100644 index 0000000..1808226 --- /dev/null +++ b/cmd/wmi.go @@ -0,0 +1,142 @@ +package cmd + +import ( + "encoding/json" + "fmt" + "github.com/FalconOpsLLC/goexec/internal/exec" + wmiexec "github.com/FalconOpsLLC/goexec/internal/exec/wmi" + "github.com/spf13/cobra" + "regexp" + "strings" +) + +func wmiCmdInit() { + wmiCustomCmdInit() + wmiCmd.AddCommand(wmiCustomCmd) + wmiProcessCmdInit() + wmiCmd.AddCommand(wmiProcessCmd) +} + +func wmiCustomCmdInit() { + wmiCustomCmd.Flags().StringVarP(&wmiArgMethod, "method", "m", "", `WMI Method to use in the format CLASS.METHOD (i.e. "Win32_Process.Create")`) + wmiCustomCmd.Flags().StringVarP(&wmiArgMethodArgs, "args", "A", "{}", `WMI Method argument(s) in JSON dictionary format (i.e. {"CommandLine":"calc.exe"})`) + if err := wmiCustomCmd.MarkFlagRequired("method"); err != nil { + panic(err) + } +} + +func wmiProcessCmdInit() { + wmiProcessCmd.Flags().StringVarP(&command, "command", "c", "", "Process command line") + wmiProcessCmd.Flags().StringVarP(&workingDirectory, "directory", "d", `C:\`, "Working directory") + if err := wmiProcessCmd.MarkFlagRequired("command"); err != nil { + panic(err) + } +} + +var ( + // for custom method + wmiArgMethod string + wmiArgMethodArgs string + + wmiClass string + wmiMethod string + wmiMethodArgsMap map[string]any + methodRegex = regexp.MustCompile(`^\w+\.\w+$`) + + wmiCmd = &cobra.Command{ + Use: "wmi", + Short: "Establish execution via WMI", + Args: cobra.NoArgs, + } + wmiCustomCmd = &cobra.Command{ + Use: "custom", + Short: "Execute specified WMI method", + Long: `Description: + The custom method creates an instance of the specified WMI class (-c), + then calls the provided method (-m) with the provided arguments (-A). + +References: + https://learn.microsoft.com/en-us/windows/win32/wmisdk/wmi-classes + `, + Args: func(cmd *cobra.Command, args []string) (err error) { + if err = needsTarget(cmd, args); err == nil { + if wmiArgMethod != "" && !methodRegex.MatchString(wmiArgMethod) { + return fmt.Errorf("invalid CLASS.METHOD syntax: %s", wmiArgMethod) + } + if err = json.Unmarshal([]byte(wmiArgMethodArgs), &wmiMethodArgsMap); err != nil { + err = fmt.Errorf("failed to parse JSON arguments: %w", err) + } + } + return + }, + Run: func(cmd *cobra.Command, args []string) { + module := wmiexec.Module{} + + connCfg := &exec.ConnectionConfig{} + cleanCfg := &exec.CleanupConfig{} + + parts := strings.SplitN(wmiArgMethod, ".", 2) + wmiClass = parts[0] + wmiMethod = parts[1] + + execCfg := &exec.ExecutionConfig{ + ExecutableName: executable, + ExecutableArgs: executableArgs, + ExecutionMethod: wmiexec.MethodCustom, + ExecutionMethodConfig: wmiexec.MethodCustomConfig{ + Class: wmiClass, + Method: wmiMethod, + Arguments: wmiMethodArgsMap, + }, + } + if err := module.Connect(log.WithContext(ctx), creds, target, connCfg); err != nil { + log.Fatal().Err(err).Msg("Connection failed") + + } else if err := module.Exec(log.WithContext(ctx), execCfg); err != nil { + log.Fatal().Err(err).Msg("Execution failed") + + } else if err := module.Cleanup(log.WithContext(ctx), cleanCfg); err != nil { + log.Error().Err(err).Msg("Cleanup failed") + } + }, + } + + wmiProcessCmd = &cobra.Command{ + Use: "process", + Short: "Create a Windows process", + Long: `Description: + The process method creates an instance of the Win32_Process WMI class, + then calls the Win32_Process.Create method with the provided command (-c), + and optional working directory (-d). + +References: + https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/create-method-in-class-win32-process +`, + Args: needsTarget, + Run: func(cmd *cobra.Command, args []string) { + module := wmiexec.Module{} + + connCfg := &exec.ConnectionConfig{} + cleanCfg := &exec.CleanupConfig{} + + execCfg := &exec.ExecutionConfig{ + ExecutableName: executable, + ExecutableArgs: executableArgs, + ExecutionMethod: wmiexec.MethodProcess, + ExecutionMethodConfig: wmiexec.MethodProcessConfig{ + Command: command, + WorkingDirectory: workingDirectory, + }, + } + if err := module.Connect(log.WithContext(ctx), creds, target, connCfg); err != nil { + log.Fatal().Err(err).Msg("Connection failed") + + } else if err := module.Exec(log.WithContext(ctx), execCfg); err != nil { + log.Fatal().Err(err).Msg("Execution failed") + + } else if err := module.Cleanup(log.WithContext(ctx), cleanCfg); err != nil { + log.Error().Err(err).Msg("Cleanup failed") + } + }, + } +) |