aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorBryan McNulty <bryanmcnulty@protonmail.com>2025-03-07 08:52:48 -0600
committerBryan McNulty <bryanmcnulty@protonmail.com>2025-03-07 08:52:48 -0600
commite87dd341dde93c289b6774f636e6767476b84a79 (patch)
tree3181b18f79b587bd04d98ed886f3505f37faeb2d /cmd
parenta5c860b8ab24c198b7390fbde90044754e35c1c5 (diff)
downloadgoexec-e87dd341dde93c289b6774f636e6767476b84a79.tar.gz
goexec-e87dd341dde93c289b6774f636e6767476b84a79.zip
Added wmiexec module + updated TODO
Diffstat (limited to 'cmd')
-rw-r--r--cmd/root.go122
-rw-r--r--cmd/wmi.go142
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")
+ }
+ },
+ }
+)