From 18c417feb4e36fbec8e2b5556e1e78b12dd288eb Mon Sep 17 00:00:00 2001 From: Bryan McNulty Date: Sat, 12 Jul 2025 15:24:02 -0500 Subject: [PATCH] Update indent style --- cmd/args.go | 196 ++++++++-------- cmd/dcom.go | 270 ++++++++++----------- cmd/scmr.go | 322 +++++++++++++------------- cmd/wmi.go | 198 ++++++++-------- internal/util/util.go | 38 +-- main.go | 2 +- pkg/goexec/dcom/dcom.go | 46 ++-- pkg/goexec/dcom/mmc.go | 62 ++--- pkg/goexec/dcom/module.go | 216 ++++++++--------- pkg/goexec/dcom/shellbrowserwindow.go | 62 ++--- pkg/goexec/dcom/shellwindows.go | 96 ++++---- pkg/goexec/dcom/util.go | 122 +++++----- pkg/goexec/io.go | 114 ++++----- pkg/goexec/scmr/change.go | 226 +++++++++--------- pkg/goexec/scmr/create.go | 184 +++++++-------- pkg/goexec/scmr/module.go | 254 ++++++++++---------- pkg/goexec/scmr/scmr.go | 96 ++++---- pkg/goexec/tsch/change.go | 220 +++++++++--------- pkg/goexec/tsch/demand.go | 118 +++++----- pkg/goexec/tsch/module.go | 242 +++++++++---------- pkg/goexec/wmi/module.go | 208 ++++++++--------- pkg/goexec/wmi/proc.go | 78 +++---- 22 files changed, 1685 insertions(+), 1685 deletions(-) diff --git a/cmd/args.go b/cmd/args.go index f33cc5b..0a0ff1f 100644 --- a/cmd/args.go +++ b/cmd/args.go @@ -1,33 +1,33 @@ package cmd import ( - "context" - "encoding/json" - "errors" - "fmt" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "os" + "context" + "encoding/json" + "errors" + "fmt" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "os" ) func registerLoggingFlags(fs *pflag.FlagSet) { - fs.SortFlags = false - fs.BoolVarP(&logDebug, "debug", "D", false, "Enable debug logging") - fs.StringVarP(&logOutput, "log-file", "O", "", "Write JSON logging output to `file`") - fs.BoolVarP(&logJson, "json", "j", false, "Write logging output in JSON lines") - fs.BoolVarP(&logQuiet, "quiet", "q", false, "Disable info logging") + fs.SortFlags = false + fs.BoolVarP(&logDebug, "debug", "D", false, "Enable debug logging") + fs.StringVarP(&logOutput, "log-file", "O", "", "Write JSON logging output to `file`") + fs.BoolVarP(&logJson, "json", "j", false, "Write logging output in JSON lines") + fs.BoolVarP(&logQuiet, "quiet", "q", false, "Disable info logging") } func registerNetworkFlags(fs *pflag.FlagSet) { - fs.StringVarP(&proxy, "proxy", "x", "", "Proxy `URI`") - fs.StringVarP(&rpcClient.Filter, "epm-filter", "F", "", "String binding to filter endpoints returned by the RPC endpoint mapper (EPM)") - fs.StringVar(&rpcClient.Endpoint, "endpoint", "", "Explicit RPC endpoint definition") - fs.BoolVar(&rpcClient.NoEpm, "no-epm", false, "Do not use EPM to automatically detect RPC endpoints") - fs.BoolVar(&rpcClient.NoSign, "no-sign", false, "Disable signing on DCERPC messages") - fs.BoolVar(&rpcClient.NoSeal, "no-seal", false, "Disable packet stub encryption on DCERPC messages") + fs.StringVarP(&proxy, "proxy", "x", "", "Proxy `URI`") + fs.StringVarP(&rpcClient.Filter, "epm-filter", "F", "", "String binding to filter endpoints returned by the RPC endpoint mapper (EPM)") + fs.StringVar(&rpcClient.Endpoint, "endpoint", "", "Explicit RPC endpoint definition") + fs.BoolVar(&rpcClient.NoEpm, "no-epm", false, "Do not use EPM to automatically detect RPC endpoints") + fs.BoolVar(&rpcClient.NoSign, "no-sign", false, "Disable signing on DCERPC messages") + fs.BoolVar(&rpcClient.NoSeal, "no-seal", false, "Disable packet stub encryption on DCERPC messages") - //cmd.MarkFlagsMutuallyExclusive("endpoint", "epm-filter") - //cmd.MarkFlagsMutuallyExclusive("no-epm", "epm-filter") + //cmd.MarkFlagsMutuallyExclusive("endpoint", "epm-filter") + //cmd.MarkFlagsMutuallyExclusive("no-epm", "epm-filter") } // FUTURE: automatically stage & execute file @@ -39,120 +39,120 @@ func registerStageFlags(fs *pflag.FlagSet) { */ func registerExecutionFlags(fs *pflag.FlagSet) { - fs.StringVarP(&exec.Input.Executable, "exec", "e", "", "Remote Windows executable to invoke") - fs.StringVarP(&exec.Input.Arguments, "args", "a", "", "Process command line arguments") - fs.StringVarP(&exec.Input.Command, "command", "c", "", "Windows process command line (executable & arguments)") + fs.StringVarP(&exec.Input.Executable, "exec", "e", "", "Remote Windows executable to invoke") + fs.StringVarP(&exec.Input.Arguments, "args", "a", "", "Process command line arguments") + fs.StringVarP(&exec.Input.Command, "command", "c", "", "Windows process command line (executable & arguments)") - //cmd.MarkFlagsOneRequired("executable", "command") - //cmd.MarkFlagsMutuallyExclusive("executable", "command") + //cmd.MarkFlagsOneRequired("executable", "command") + //cmd.MarkFlagsMutuallyExclusive("executable", "command") } func registerExecutionOutputFlags(fs *pflag.FlagSet) { - fs.StringVarP(&outputPath, "out", "o", "", `Fetch execution output to file or "-" for standard output`) - fs.StringVarP(&outputMethod, "out-method", "m", "smb", "Method to fetch execution output") - //fs.StringVar(&exec.Output.RemotePath, "out-remote", "", "Location to temporarily store output on remote filesystem") - fs.BoolVar(&exec.Output.NoDelete, "no-delete-out", false, "Preserve output file on remote filesystem") + fs.StringVarP(&outputPath, "out", "o", "", `Fetch execution output to file or "-" for standard output`) + fs.StringVarP(&outputMethod, "out-method", "m", "smb", "Method to fetch execution output") + //fs.StringVar(&exec.Output.RemotePath, "out-remote", "", "Location to temporarily store output on remote filesystem") + fs.BoolVar(&exec.Output.NoDelete, "no-delete-out", false, "Preserve output file on remote filesystem") } func args(reqs ...func(*cobra.Command, []string) error) (fn func(*cobra.Command, []string) error) { - return func(cmd *cobra.Command, args []string) (err error) { + return func(cmd *cobra.Command, args []string) (err error) { - for _, req := range reqs { - if err = req(cmd, args); err != nil { - return - } - } - return - } + for _, req := range reqs { + if err = req(cmd, args); err != nil { + return + } + } + return + } } func argsAcceptValues(name string, in *string, valid ...string) func(*cobra.Command, []string) error { - return func(*cobra.Command, []string) error { - for _, v := range valid { - if *in == v { - return nil - } - } - if j, err := json.Marshal(valid); err == nil { - return fmt.Errorf("parse %s: %q doesn't match any accepted values: %s", name, *in, string(j)) - } else { - return err - } - } + return func(*cobra.Command, []string) error { + for _, v := range valid { + if *in == v { + return nil + } + } + if j, err := json.Marshal(valid); err == nil { + return fmt.Errorf("parse %s: %q doesn't match any accepted values: %s", name, *in, string(j)) + } else { + return err + } + } } func argsTarget(proto string) func(cmd *cobra.Command, args []string) error { - return func(cmd *cobra.Command, args []string) (err error) { + return func(cmd *cobra.Command, args []string) (err error) { - if len(args) != 1 { - return errors.New("command require exactly one positional argument: [target]") - } + if len(args) != 1 { + return errors.New("command require exactly one positional argument: [target]") + } - if credential, target, err = adAuthOpts.WithTarget(context.TODO(), proto, args[0]); err != nil { - return fmt.Errorf("failed to parse target: %w", err) - } + if credential, target, err = adAuthOpts.WithTarget(context.TODO(), proto, args[0]); err != nil { + return fmt.Errorf("failed to parse target: %w", err) + } - if credential == nil { - return errors.New("no credentials supplied") - } - if target == nil { - return errors.New("no target supplied") - } - return - } + if credential == nil { + return errors.New("no credentials supplied") + } + if target == nil { + return errors.New("no target supplied") + } + return + } } func argsSmbClient() func(cmd *cobra.Command, args []string) error { - return args( - argsTarget("cifs"), + return args( + argsTarget("cifs"), - func(_ *cobra.Command, _ []string) error { + func(_ *cobra.Command, _ []string) error { - smbClient.Credential = credential - smbClient.Target = target - smbClient.Proxy = proxy + smbClient.Credential = credential + smbClient.Target = target + smbClient.Proxy = proxy - return smbClient.Parse(context.TODO()) - }, - ) + return smbClient.Parse(context.TODO()) + }, + ) } func argsRpcClient(proto string) func(cmd *cobra.Command, args []string) error { - return args( - argsTarget(proto), + return args( + argsTarget(proto), - func(cmd *cobra.Command, args []string) (err error) { + func(cmd *cobra.Command, args []string) (err error) { - rpcClient.Target = target - rpcClient.Credential = credential - rpcClient.Proxy = proxy + rpcClient.Target = target + rpcClient.Credential = credential + rpcClient.Proxy = proxy - return rpcClient.Parse(context.TODO()) - }, - ) + return rpcClient.Parse(context.TODO()) + }, + ) } func argsOutput(methods ...string) func(cmd *cobra.Command, args []string) error { - var as []func(*cobra.Command, []string) error + var as []func(*cobra.Command, []string) error - for _, method := range methods { - if method == "smb" { - as = append(as, argsSmbClient()) - } - } + for _, method := range methods { + if method == "smb" { + as = append(as, argsSmbClient()) + } + } - return args(append(as, func(*cobra.Command, []string) (err error) { + return args(append(as, func(*cobra.Command, []string) (err error) { - if outputPath != "" { - if outputPath == "-" { - exec.Output.Writer = os.Stdout + if outputPath != "" { + if outputPath == "-" { + exec.Output.Writer = os.Stdout - } else if exec.Output.Writer, err = os.OpenFile(outputPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil { - log.Fatal().Err(err).Msg("Failed to open output file") - } - } - return - })...) + } else if exec.Output.Writer, err = os.OpenFile(outputPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil { + log.Fatal().Err(err).Msg("Failed to open output file") + } + } + return + })...) } diff --git a/cmd/dcom.go b/cmd/dcom.go index 30c32bc..1b06bc9 100644 --- a/cmd/dcom.go +++ b/cmd/dcom.go @@ -1,184 +1,184 @@ package cmd import ( - "context" - "github.com/FalconOpsLLC/goexec/pkg/goexec" - dcomexec "github.com/FalconOpsLLC/goexec/pkg/goexec/dcom" - "github.com/oiweiwei/go-msrpc/ssp/gssapi" - "github.com/spf13/cobra" + "context" + "github.com/FalconOpsLLC/goexec/pkg/goexec" + dcomexec "github.com/FalconOpsLLC/goexec/pkg/goexec/dcom" + "github.com/oiweiwei/go-msrpc/ssp/gssapi" + "github.com/spf13/cobra" ) func dcomCmdInit() { - cmdFlags[dcomCmd] = []*flagSet{ - defaultAuthFlags, - defaultLogFlags, - defaultNetRpcFlags, - } - dcomMmcCmdInit() - dcomShellWindowsCmdInit() - dcomShellBrowserWindowCmdInit() + cmdFlags[dcomCmd] = []*flagSet{ + defaultAuthFlags, + defaultLogFlags, + defaultNetRpcFlags, + } + dcomMmcCmdInit() + dcomShellWindowsCmdInit() + dcomShellBrowserWindowCmdInit() - dcomCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags) - dcomCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags) - dcomCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags) - dcomCmd.AddCommand(dcomMmcCmd, dcomShellWindowsCmd, dcomShellBrowserWindowCmd) + dcomCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags) + dcomCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags) + dcomCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags) + dcomCmd.AddCommand(dcomMmcCmd, dcomShellWindowsCmd, dcomShellBrowserWindowCmd) } func dcomMmcCmdInit() { - dcomMmcExecFlags := newFlagSet("Execution") + dcomMmcExecFlags := newFlagSet("Execution") - registerExecutionFlags(dcomMmcExecFlags.Flags) - registerExecutionOutputFlags(dcomMmcExecFlags.Flags) + registerExecutionFlags(dcomMmcExecFlags.Flags) + registerExecutionOutputFlags(dcomMmcExecFlags.Flags) - dcomMmcExecFlags.Flags.StringVar(&dcomMmc.WorkingDirectory, "directory", `C:\`, "Working `directory`") - dcomMmcExecFlags.Flags.StringVar(&dcomMmc.WindowState, "window", "Minimized", "Window state") + dcomMmcExecFlags.Flags.StringVar(&dcomMmc.WorkingDirectory, "directory", `C:\`, "Working `directory`") + dcomMmcExecFlags.Flags.StringVar(&dcomMmc.WindowState, "window", "Minimized", "Window state") - cmdFlags[dcomMmcCmd] = []*flagSet{ - dcomMmcExecFlags, - defaultAuthFlags, - defaultLogFlags, - defaultNetRpcFlags, - } - dcomMmcCmd.Flags().AddFlagSet(dcomMmcExecFlags.Flags) + cmdFlags[dcomMmcCmd] = []*flagSet{ + dcomMmcExecFlags, + defaultAuthFlags, + defaultLogFlags, + defaultNetRpcFlags, + } + dcomMmcCmd.Flags().AddFlagSet(dcomMmcExecFlags.Flags) - // Constraints - dcomMmcCmd.MarkFlagsOneRequired("command", "exec") + // Constraints + dcomMmcCmd.MarkFlagsOneRequired("command", "exec") } func dcomShellWindowsCmdInit() { - dcomShellWindowsExecFlags := newFlagSet("Execution") + dcomShellWindowsExecFlags := newFlagSet("Execution") - registerExecutionFlags(dcomShellWindowsExecFlags.Flags) - registerExecutionOutputFlags(dcomShellWindowsExecFlags.Flags) + registerExecutionFlags(dcomShellWindowsExecFlags.Flags) + registerExecutionOutputFlags(dcomShellWindowsExecFlags.Flags) - dcomShellWindowsExecFlags.Flags.StringVar(&dcomShellWindows.WorkingDirectory, "directory", `C:\`, "Working `directory`") - dcomShellWindowsExecFlags.Flags.StringVar(&dcomShellWindows.WindowState, "app-window", "0", "Application window state `ID`") + dcomShellWindowsExecFlags.Flags.StringVar(&dcomShellWindows.WorkingDirectory, "directory", `C:\`, "Working `directory`") + dcomShellWindowsExecFlags.Flags.StringVar(&dcomShellWindows.WindowState, "app-window", "0", "Application window state `ID`") - cmdFlags[dcomShellWindowsCmd] = []*flagSet{ - dcomShellWindowsExecFlags, - defaultAuthFlags, - defaultLogFlags, - defaultNetRpcFlags, - } - dcomShellWindowsCmd.Flags().AddFlagSet(dcomShellWindowsExecFlags.Flags) + cmdFlags[dcomShellWindowsCmd] = []*flagSet{ + dcomShellWindowsExecFlags, + defaultAuthFlags, + defaultLogFlags, + defaultNetRpcFlags, + } + dcomShellWindowsCmd.Flags().AddFlagSet(dcomShellWindowsExecFlags.Flags) - // Constraints - dcomShellWindowsCmd.MarkFlagsOneRequired("command", "exec") + // Constraints + dcomShellWindowsCmd.MarkFlagsOneRequired("command", "exec") } func dcomShellBrowserWindowCmdInit() { - dcomShellBrowserWindowExecFlags := newFlagSet("Execution") + dcomShellBrowserWindowExecFlags := newFlagSet("Execution") - registerExecutionFlags(dcomShellBrowserWindowExecFlags.Flags) - registerExecutionOutputFlags(dcomShellBrowserWindowExecFlags.Flags) + registerExecutionFlags(dcomShellBrowserWindowExecFlags.Flags) + registerExecutionOutputFlags(dcomShellBrowserWindowExecFlags.Flags) - dcomShellBrowserWindowExecFlags.Flags.StringVar(&dcomShellBrowserWindow.WorkingDirectory, "directory", `C:\`, "Working `directory`") - dcomShellBrowserWindowExecFlags.Flags.StringVar(&dcomShellBrowserWindow.WindowState, "app-window", "0", "Application window state `ID`") + dcomShellBrowserWindowExecFlags.Flags.StringVar(&dcomShellBrowserWindow.WorkingDirectory, "directory", `C:\`, "Working `directory`") + dcomShellBrowserWindowExecFlags.Flags.StringVar(&dcomShellBrowserWindow.WindowState, "app-window", "0", "Application window state `ID`") - cmdFlags[dcomShellBrowserWindowCmd] = []*flagSet{ - dcomShellBrowserWindowExecFlags, - defaultAuthFlags, - defaultLogFlags, - defaultNetRpcFlags, - } - dcomShellBrowserWindowCmd.Flags().AddFlagSet(dcomShellBrowserWindowExecFlags.Flags) + cmdFlags[dcomShellBrowserWindowCmd] = []*flagSet{ + dcomShellBrowserWindowExecFlags, + defaultAuthFlags, + defaultLogFlags, + defaultNetRpcFlags, + } + dcomShellBrowserWindowCmd.Flags().AddFlagSet(dcomShellBrowserWindowExecFlags.Flags) - // Constraints - dcomShellBrowserWindowCmd.MarkFlagsOneRequired("command", "exec") + // Constraints + dcomShellBrowserWindowCmd.MarkFlagsOneRequired("command", "exec") } var ( - dcomMmc dcomexec.DcomMmc - dcomShellWindows dcomexec.DcomShellWindows - dcomShellBrowserWindow dcomexec.DcomShellBrowserWindow + dcomMmc dcomexec.DcomMmc + dcomShellWindows dcomexec.DcomShellWindows + dcomShellBrowserWindow dcomexec.DcomShellBrowserWindow - dcomCmd = &cobra.Command{ - Use: "dcom", - Short: "Execute with Distributed Component Object Model (MS-DCOM)", - Long: `Description: + dcomCmd = &cobra.Command{ + Use: "dcom", + Short: "Execute with Distributed Component Object Model (MS-DCOM)", + Long: `Description: The dcom module uses exposed Distributed Component Object Model (DCOM) objects to spawn processes.`, - GroupID: "module", - Args: cobra.NoArgs, - } + GroupID: "module", + Args: cobra.NoArgs, + } - dcomMmcCmd = &cobra.Command{ - Use: "mmc [target]", - Short: "Execute with the MMC20.Application DCOM object", - Long: `Description: + dcomMmcCmd = &cobra.Command{ + Use: "mmc [target]", + Short: "Execute with the MMC20.Application DCOM object", + Long: `Description: The mmc method uses the exposed MMC20.Application object to call Document.ActiveView.ShellExec, and ultimately spawn a process on the remote host.`, - Args: args( - argsRpcClient("host"), - argsOutput("smb"), - argsAcceptValues("window", &dcomMmc.WindowState, "Minimized", "Maximized", "Restored"), - ), - Run: func(cmd *cobra.Command, args []string) { - dcomMmc.Client = &rpcClient - dcomMmc.IO = exec - dcomMmc.ClassID = dcomexec.Mmc20Uuid + Args: args( + argsRpcClient("host"), + argsOutput("smb"), + argsAcceptValues("window", &dcomMmc.WindowState, "Minimized", "Maximized", "Restored"), + ), + Run: func(cmd *cobra.Command, args []string) { + dcomMmc.Client = &rpcClient + dcomMmc.IO = exec + dcomMmc.ClassID = dcomexec.Mmc20Uuid - ctx := log.With(). - Str("module", dcomexec.ModuleName). - Str("method", dcomexec.MethodMmc). - Logger().WithContext(gssapi.NewSecurityContext(context.Background())) + ctx := log.With(). + Str("module", dcomexec.ModuleName). + Str("method", dcomexec.MethodMmc). + Logger().WithContext(gssapi.NewSecurityContext(context.Background())) - if err := goexec.ExecuteCleanMethod(ctx, &dcomMmc, &exec); err != nil { - log.Fatal().Err(err).Msg("Operation failed") - } - }, - } + if err := goexec.ExecuteCleanMethod(ctx, &dcomMmc, &exec); err != nil { + log.Fatal().Err(err).Msg("Operation failed") + } + }, + } - dcomShellWindowsCmd = &cobra.Command{ - Use: "shellwindows [target]", - Short: "Execute with the ShellWindows DCOM object", - Long: `Description: + dcomShellWindowsCmd = &cobra.Command{ + Use: "shellwindows [target]", + Short: "Execute with the ShellWindows DCOM object", + Long: `Description: The shellwindows method uses the exposed ShellWindows DCOM object on older Windows installations to call Item().Document.Application.ShellExecute, and spawn the provided process.`, - Args: args( - argsRpcClient("host"), - argsOutput("smb"), - argsAcceptValues("app-window", &dcomShellWindows.WindowState, "0", "1", "2", "3", "4", "5", "7", "10"), - ), - Run: func(cmd *cobra.Command, args []string) { - dcomShellWindows.Client = &rpcClient - dcomShellWindows.IO = exec - dcomShellWindows.ClassID = dcomexec.ShellWindowsUuid + Args: args( + argsRpcClient("host"), + argsOutput("smb"), + argsAcceptValues("app-window", &dcomShellWindows.WindowState, "0", "1", "2", "3", "4", "5", "7", "10"), + ), + Run: func(cmd *cobra.Command, args []string) { + dcomShellWindows.Client = &rpcClient + dcomShellWindows.IO = exec + dcomShellWindows.ClassID = dcomexec.ShellWindowsUuid - ctx := log.With(). - Str("module", dcomexec.ModuleName). - Str("method", dcomexec.MethodShellWindows). - Logger().WithContext(gssapi.NewSecurityContext(context.Background())) + ctx := log.With(). + Str("module", dcomexec.ModuleName). + Str("method", dcomexec.MethodShellWindows). + Logger().WithContext(gssapi.NewSecurityContext(context.Background())) - if err := goexec.ExecuteCleanMethod(ctx, &dcomShellWindows, &exec); err != nil { - log.Fatal().Err(err).Msg("Operation failed") - } - }, - } + if err := goexec.ExecuteCleanMethod(ctx, &dcomShellWindows, &exec); err != nil { + log.Fatal().Err(err).Msg("Operation failed") + } + }, + } - dcomShellBrowserWindowCmd = &cobra.Command{ - Use: "shellbrowserwindow [target]", - Short: "Execute with the ShellBrowserWindow DCOM object", - Long: `Description: + dcomShellBrowserWindowCmd = &cobra.Command{ + Use: "shellbrowserwindow [target]", + Short: "Execute with the ShellBrowserWindow DCOM object", + Long: `Description: The shellbrowserwindow method uses the exposed ShellBrowserWindow DCOM object on older Windows installations to call Document.Application.ShellExecute, and spawn the provided process.`, - Args: args( - argsRpcClient("host"), - argsOutput("smb"), - argsAcceptValues("app-window", &dcomShellBrowserWindow.WindowState, "0", "1", "2", "3", "4", "5", "7", "10"), - ), - Run: func(cmd *cobra.Command, args []string) { - dcomShellBrowserWindow.Client = &rpcClient - dcomShellBrowserWindow.IO = exec - dcomShellBrowserWindow.ClassID = dcomexec.ShellBrowserWindowUuid + Args: args( + argsRpcClient("host"), + argsOutput("smb"), + argsAcceptValues("app-window", &dcomShellBrowserWindow.WindowState, "0", "1", "2", "3", "4", "5", "7", "10"), + ), + Run: func(cmd *cobra.Command, args []string) { + dcomShellBrowserWindow.Client = &rpcClient + dcomShellBrowserWindow.IO = exec + dcomShellBrowserWindow.ClassID = dcomexec.ShellBrowserWindowUuid - ctx := log.With(). - Str("module", dcomexec.ModuleName). - Str("method", dcomexec.MethodShellBrowserWindow). - Logger().WithContext(gssapi.NewSecurityContext(context.Background())) + ctx := log.With(). + Str("module", dcomexec.ModuleName). + Str("method", dcomexec.MethodShellBrowserWindow). + Logger().WithContext(gssapi.NewSecurityContext(context.Background())) - if err := goexec.ExecuteCleanMethod(ctx, &dcomShellBrowserWindow, &exec); err != nil { - log.Fatal().Err(err).Msg("Operation failed") - } - }, - } + if err := goexec.ExecuteCleanMethod(ctx, &dcomShellBrowserWindow, &exec); err != nil { + log.Fatal().Err(err).Msg("Operation failed") + } + }, + } ) diff --git a/cmd/scmr.go b/cmd/scmr.go index 23d9272..595f8a6 100644 --- a/cmd/scmr.go +++ b/cmd/scmr.go @@ -1,222 +1,222 @@ package cmd import ( - "context" - "github.com/FalconOpsLLC/goexec/internal/util" - "github.com/FalconOpsLLC/goexec/pkg/goexec" - "github.com/oiweiwei/go-msrpc/ssp/gssapi" - "github.com/spf13/cobra" + "context" + "github.com/FalconOpsLLC/goexec/internal/util" + "github.com/FalconOpsLLC/goexec/pkg/goexec" + "github.com/oiweiwei/go-msrpc/ssp/gssapi" + "github.com/spf13/cobra" - scmrexec "github.com/FalconOpsLLC/goexec/pkg/goexec/scmr" + scmrexec "github.com/FalconOpsLLC/goexec/pkg/goexec/scmr" ) func scmrCmdInit() { - cmdFlags[scmrCmd] = []*flagSet{ - defaultAuthFlags, - defaultLogFlags, - defaultNetRpcFlags, - } - scmrCreateCmdInit() - scmrChangeCmdInit() - scmrDeleteCmdInit() + cmdFlags[scmrCmd] = []*flagSet{ + defaultAuthFlags, + defaultLogFlags, + defaultNetRpcFlags, + } + scmrCreateCmdInit() + scmrChangeCmdInit() + scmrDeleteCmdInit() - scmrCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags) - scmrCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags) - scmrCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags) - scmrCmd.AddCommand(scmrCreateCmd, scmrChangeCmd, scmrDeleteCmd) + scmrCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags) + scmrCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags) + scmrCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags) + scmrCmd.AddCommand(scmrCreateCmd, scmrChangeCmd, scmrDeleteCmd) } func scmrCreateCmdInit() { - scmrCreateFlags := newFlagSet("Service") + scmrCreateFlags := newFlagSet("Service") - scmrCreateFlags.Flags.StringVarP(&scmrCreate.DisplayName, "display-name", "n", "", "Display name of service to create") - scmrCreateFlags.Flags.StringVarP(&scmrCreate.ServiceName, "service", "s", "", "Name of service to create") - scmrCreateFlags.Flags.BoolVar(&scmrCreate.NoDelete, "no-delete", false, "Don't delete service after execution") - scmrCreateFlags.Flags.BoolVar(&scmrCreate.NoStart, "no-start", false, "Don't start service") + scmrCreateFlags.Flags.StringVarP(&scmrCreate.DisplayName, "display-name", "n", "", "Display name of service to create") + scmrCreateFlags.Flags.StringVarP(&scmrCreate.ServiceName, "service", "s", "", "Name of service to create") + scmrCreateFlags.Flags.BoolVar(&scmrCreate.NoDelete, "no-delete", false, "Don't delete service after execution") + scmrCreateFlags.Flags.BoolVar(&scmrCreate.NoStart, "no-start", false, "Don't start service") - scmrCreateExecFlags := newFlagSet("Execution") + scmrCreateExecFlags := newFlagSet("Execution") - // TODO: SCMR output - //registerExecutionOutputFlags(scmrCreateExecFlags.Flags) + // TODO: SCMR output + //registerExecutionOutputFlags(scmrCreateExecFlags.Flags) - scmrCreateExecFlags.Flags.StringVarP(&exec.Input.ExecutablePath, "executable-path", "f", "", "Full path to a remote Windows executable") - scmrCreateExecFlags.Flags.StringVarP(&exec.Input.Arguments, "args", "a", "", "Arguments to pass to the executable") + scmrCreateExecFlags.Flags.StringVarP(&exec.Input.ExecutablePath, "executable-path", "f", "", "Full path to a remote Windows executable") + scmrCreateExecFlags.Flags.StringVarP(&exec.Input.Arguments, "args", "a", "", "Arguments to pass to the executable") - scmrCreateCmd.Flags().AddFlagSet(scmrCreateFlags.Flags) - scmrCreateCmd.Flags().AddFlagSet(scmrCreateExecFlags.Flags) + scmrCreateCmd.Flags().AddFlagSet(scmrCreateFlags.Flags) + scmrCreateCmd.Flags().AddFlagSet(scmrCreateExecFlags.Flags) - cmdFlags[scmrCreateCmd] = []*flagSet{ - scmrCreateExecFlags, - scmrCreateFlags, - defaultAuthFlags, - defaultLogFlags, - defaultNetRpcFlags, - } + cmdFlags[scmrCreateCmd] = []*flagSet{ + scmrCreateExecFlags, + scmrCreateFlags, + defaultAuthFlags, + defaultLogFlags, + defaultNetRpcFlags, + } - // Constraints - { - //scmrCreateCmd.MarkFlagsMutuallyExclusive("no-delete", "no-start") - if err := scmrCreateCmd.MarkFlagRequired("executable-path"); err != nil { - panic(err) - } - } + // Constraints + { + //scmrCreateCmd.MarkFlagsMutuallyExclusive("no-delete", "no-start") + if err := scmrCreateCmd.MarkFlagRequired("executable-path"); err != nil { + panic(err) + } + } } func scmrChangeCmdInit() { - scmrChangeFlags := newFlagSet("Service Control") + scmrChangeFlags := newFlagSet("Service Control") - scmrChangeFlags.Flags.StringVarP(&scmrChange.ServiceName, "service-name", "s", "", "Name of service to modify") - scmrChangeFlags.Flags.BoolVar(&scmrChange.NoStart, "no-start", false, "Don't start service") + scmrChangeFlags.Flags.StringVarP(&scmrChange.ServiceName, "service-name", "s", "", "Name of service to modify") + scmrChangeFlags.Flags.BoolVar(&scmrChange.NoStart, "no-start", false, "Don't start service") - scmrChangeExecFlags := newFlagSet("Execution") + scmrChangeExecFlags := newFlagSet("Execution") - scmrChangeExecFlags.Flags.StringVarP(&exec.Input.ExecutablePath, "executable-path", "f", "", "Full path to remote Windows executable") - scmrChangeExecFlags.Flags.StringVarP(&exec.Input.Arguments, "args", "a", "", "Arguments to pass to executable") + scmrChangeExecFlags.Flags.StringVarP(&exec.Input.ExecutablePath, "executable-path", "f", "", "Full path to remote Windows executable") + scmrChangeExecFlags.Flags.StringVarP(&exec.Input.Arguments, "args", "a", "", "Arguments to pass to executable") - // TODO: SCMR output - //registerExecutionOutputFlags(scmrChangeExecFlags.Flags) - //registerStageFlags(scmrChangeExecFlags.Flags) + // TODO: SCMR output + //registerExecutionOutputFlags(scmrChangeExecFlags.Flags) + //registerStageFlags(scmrChangeExecFlags.Flags) - cmdFlags[scmrChangeCmd] = []*flagSet{ - scmrChangeFlags, - scmrChangeExecFlags, - defaultAuthFlags, - defaultLogFlags, - defaultNetRpcFlags, - } + cmdFlags[scmrChangeCmd] = []*flagSet{ + scmrChangeFlags, + scmrChangeExecFlags, + defaultAuthFlags, + defaultLogFlags, + defaultNetRpcFlags, + } - scmrChangeCmd.Flags().AddFlagSet(scmrChangeFlags.Flags) - scmrChangeCmd.Flags().AddFlagSet(scmrChangeExecFlags.Flags) + scmrChangeCmd.Flags().AddFlagSet(scmrChangeFlags.Flags) + scmrChangeCmd.Flags().AddFlagSet(scmrChangeExecFlags.Flags) - // Constraints - { - if err := scmrChangeCmd.MarkFlagRequired("service-name"); err != nil { - panic(err) - } - if err := scmrCreateCmd.MarkFlagRequired("executable-path"); err != nil { - panic(err) - } - } + // Constraints + { + if err := scmrChangeCmd.MarkFlagRequired("service-name"); err != nil { + panic(err) + } + if err := scmrCreateCmd.MarkFlagRequired("executable-path"); err != nil { + panic(err) + } + } } func scmrDeleteCmdInit() { - scmrDeleteFlags := newFlagSet("Service Control") - scmrDeleteFlags.Flags.StringVarP(&scmrDelete.ServiceName, "service-name", "s", scmrDelete.ServiceName, "Name of service to delete") + scmrDeleteFlags := newFlagSet("Service Control") + scmrDeleteFlags.Flags.StringVarP(&scmrDelete.ServiceName, "service-name", "s", scmrDelete.ServiceName, "Name of service to delete") - cmdFlags[scmrDeleteCmd] = []*flagSet{ - scmrDeleteFlags, - defaultAuthFlags, - defaultLogFlags, - defaultNetRpcFlags, - } + cmdFlags[scmrDeleteCmd] = []*flagSet{ + scmrDeleteFlags, + defaultAuthFlags, + defaultLogFlags, + defaultNetRpcFlags, + } - scmrDeleteCmd.Flags().AddFlagSet(scmrDeleteFlags.Flags) + scmrDeleteCmd.Flags().AddFlagSet(scmrDeleteFlags.Flags) - if err := scmrDeleteCmd.MarkFlagRequired("service-name"); err != nil { - panic(err) - } + if err := scmrDeleteCmd.MarkFlagRequired("service-name"); err != nil { + panic(err) + } } var ( - scmrCreate = scmrexec.ScmrCreate{} - scmrChange = scmrexec.ScmrChange{} - scmrDelete = scmrexec.ScmrDelete{} + scmrCreate = scmrexec.ScmrCreate{} + scmrChange = scmrexec.ScmrChange{} + scmrDelete = scmrexec.ScmrDelete{} - scmrCmd = &cobra.Command{ - Use: "scmr", - Short: "Execute with Service Control Manager Remote (MS-SCMR)", - Long: `Description: + scmrCmd = &cobra.Command{ + Use: "scmr", + Short: "Execute with Service Control Manager Remote (MS-SCMR)", + Long: `Description: The SCMR module works a lot like Impacket's smbexec.py, but it provides additional RPC transports to evade network monitoring or firewall rules, and some minor OPSEC improvements overall.`, - GroupID: "module", - Args: cobra.NoArgs, - } + GroupID: "module", + Args: cobra.NoArgs, + } - scmrCreateCmd = &cobra.Command{ - Use: "create [target]", - Short: "Spawn a remote process by creating & running a Windows service", - Long: `Description: + scmrCreateCmd = &cobra.Command{ + Use: "create [target]", + Short: "Spawn a remote process by creating & running a Windows service", + Long: `Description: The create method calls RCreateServiceW to create a new Windows service on the remote target with the provided executable & arguments as the lpBinaryPathName`, - Args: args( - argsRpcClient("cifs"), - argsSmbClient(), - ), + Args: args( + argsRpcClient("cifs"), + argsSmbClient(), + ), - Run: func(cmd *cobra.Command, args []string) { - scmrCreate.Client = &rpcClient - scmrCreate.IO = exec + Run: func(cmd *cobra.Command, args []string) { + scmrCreate.Client = &rpcClient + scmrCreate.IO = exec - log = log.With(). - Str("module", "scmr"). - Str("method", "create"). - Logger() + log = log.With(). + Str("module", "scmr"). + Str("method", "create"). + Logger() - // Warnings - { - if scmrCreate.ServiceName == "" { - log.Warn().Msg("No service name was provided. Using a random string") - scmrCreate.ServiceName = util.RandomString() - } - if scmrCreate.NoDelete { - log.Warn().Msg("Service will not be deleted after execution") - } - if scmrCreate.DisplayName == "" { - log.Debug().Msg("No display name specified, using service name as display name") - scmrCreate.DisplayName = scmrCreate.ServiceName - } - } + // Warnings + { + if scmrCreate.ServiceName == "" { + log.Warn().Msg("No service name was provided. Using a random string") + scmrCreate.ServiceName = util.RandomString() + } + if scmrCreate.NoDelete { + log.Warn().Msg("Service will not be deleted after execution") + } + if scmrCreate.DisplayName == "" { + log.Debug().Msg("No display name specified, using service name as display name") + scmrCreate.DisplayName = scmrCreate.ServiceName + } + } - ctx := log.WithContext(gssapi.NewSecurityContext(context.Background())) + ctx := log.WithContext(gssapi.NewSecurityContext(context.Background())) - if err := goexec.ExecuteCleanMethod(ctx, &scmrCreate, &exec); err != nil { - log.Fatal().Err(err).Msg("Operation failed") - } - }, - } + if err := goexec.ExecuteCleanMethod(ctx, &scmrCreate, &exec); err != nil { + log.Fatal().Err(err).Msg("Operation failed") + } + }, + } - scmrChangeCmd = &cobra.Command{ - Use: "change [target]", - Short: "Change an existing Windows service to spawn an arbitrary process", - Long: `Description: + scmrChangeCmd = &cobra.Command{ + Use: "change [target]", + Short: "Change an existing Windows service to spawn an arbitrary process", + Long: `Description: The change method executes programs by modifying existing Windows services using the RChangeServiceConfigW method rather than calling RCreateServiceW like scmr create. The modified service is restored to its original state after execution`, - Args: argsRpcClient("cifs"), + Args: argsRpcClient("cifs"), - Run: func(cmd *cobra.Command, args []string) { - scmrChange.Client = &rpcClient - scmrChange.IO = exec + Run: func(cmd *cobra.Command, args []string) { + scmrChange.Client = &rpcClient + scmrChange.IO = exec - ctx := log.With(). - Str("module", "scmr"). - Str("method", "change"). - Logger().WithContext(gssapi.NewSecurityContext(context.Background())) + ctx := log.With(). + Str("module", "scmr"). + Str("method", "change"). + Logger().WithContext(gssapi.NewSecurityContext(context.Background())) - if err := goexec.ExecuteCleanMethod(ctx, &scmrChange, &exec); err != nil { - log.Fatal().Err(err).Msg("Operation failed") - } - }, - } - scmrDeleteCmd = &cobra.Command{ - Use: "delete [target]", - Short: "Delete an existing Windows service", - Long: `Description: + if err := goexec.ExecuteCleanMethod(ctx, &scmrChange, &exec); err != nil { + log.Fatal().Err(err).Msg("Operation failed") + } + }, + } + scmrDeleteCmd = &cobra.Command{ + Use: "delete [target]", + Short: "Delete an existing Windows service", + Long: `Description: The delete method will simply delete the provided service.`, - Args: argsRpcClient("cifs"), - Run: func(cmd *cobra.Command, args []string) { - scmrDelete.Client = &rpcClient + Args: argsRpcClient("cifs"), + Run: func(cmd *cobra.Command, args []string) { + scmrDelete.Client = &rpcClient - ctx := log.With(). - Str("module", "scmr"). - Str("method", "delete"). - Logger().WithContext(gssapi.NewSecurityContext(context.Background())) + ctx := log.With(). + Str("module", "scmr"). + Str("method", "delete"). + Logger().WithContext(gssapi.NewSecurityContext(context.Background())) - if err := goexec.ExecuteCleanAuxiliaryMethod(ctx, &scmrDelete); err != nil { - log.Fatal().Err(err).Msg("Operation failed") - } - }, - } + if err := goexec.ExecuteCleanAuxiliaryMethod(ctx, &scmrDelete); err != nil { + log.Fatal().Err(err).Msg("Operation failed") + } + }, + } ) diff --git a/cmd/wmi.go b/cmd/wmi.go index 90bb5ad..a0f78f0 100644 --- a/cmd/wmi.go +++ b/cmd/wmi.go @@ -1,140 +1,140 @@ package cmd import ( - "context" - "encoding/json" - "github.com/FalconOpsLLC/goexec/pkg/goexec" - wmiexec "github.com/FalconOpsLLC/goexec/pkg/goexec/wmi" - "github.com/oiweiwei/go-msrpc/ssp/gssapi" - "github.com/spf13/cobra" - "os" + "context" + "encoding/json" + "github.com/FalconOpsLLC/goexec/pkg/goexec" + wmiexec "github.com/FalconOpsLLC/goexec/pkg/goexec/wmi" + "github.com/oiweiwei/go-msrpc/ssp/gssapi" + "github.com/spf13/cobra" + "os" ) func wmiCmdInit() { - cmdFlags[wmiCmd] = []*flagSet{ - defaultAuthFlags, - defaultLogFlags, - defaultNetRpcFlags, - } - wmiCallCmdInit() - wmiProcCmdInit() + cmdFlags[wmiCmd] = []*flagSet{ + defaultAuthFlags, + defaultLogFlags, + defaultNetRpcFlags, + } + wmiCallCmdInit() + wmiProcCmdInit() - wmiCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags) - wmiCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags) - wmiCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags) - wmiCmd.AddCommand(wmiProcCmd, wmiCallCmd) + wmiCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags) + wmiCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags) + wmiCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags) + wmiCmd.AddCommand(wmiProcCmd, wmiCallCmd) } func wmiCallCmdInit() { - wmiCallFlags := newFlagSet("WMI") + wmiCallFlags := newFlagSet("WMI") - wmiCallFlags.Flags.StringVarP(&wmiCall.Resource, "namespace", "n", "//./root/cimv2", "WMI namespace") - wmiCallFlags.Flags.StringVarP(&wmiCall.Class, "class", "C", "", `WMI class to instantiate (i.e. "Win32_Process")`) - wmiCallFlags.Flags.StringVarP(&wmiCall.Method, "method", "m", "", `WMI Method to call (i.e. "Create")`) - wmiCallFlags.Flags.StringVarP(&wmiArguments, "args", "A", "{}", `WMI Method argument(s) in JSON dictionary format (i.e. {"Command":"calc.exe"})`) + wmiCallFlags.Flags.StringVarP(&wmiCall.Resource, "namespace", "n", "//./root/cimv2", "WMI namespace") + wmiCallFlags.Flags.StringVarP(&wmiCall.Class, "class", "C", "", `WMI class to instantiate (i.e. "Win32_Process")`) + wmiCallFlags.Flags.StringVarP(&wmiCall.Method, "method", "m", "", `WMI Method to call (i.e. "Create")`) + wmiCallFlags.Flags.StringVarP(&wmiArguments, "args", "A", "{}", `WMI Method argument(s) in JSON dictionary format (i.e. {"Command":"calc.exe"})`) - wmiCallCmd.Flags().AddFlagSet(wmiCallFlags.Flags) + wmiCallCmd.Flags().AddFlagSet(wmiCallFlags.Flags) - cmdFlags[wmiCallCmd] = []*flagSet{ - wmiCallFlags, - defaultAuthFlags, - defaultLogFlags, - defaultNetRpcFlags, - } - if err := wmiCallCmd.MarkFlagRequired("class"); err != nil { - panic(err) - } - if err := wmiCallCmd.MarkFlagRequired("method"); err != nil { - panic(err) - } + cmdFlags[wmiCallCmd] = []*flagSet{ + wmiCallFlags, + defaultAuthFlags, + defaultLogFlags, + defaultNetRpcFlags, + } + if err := wmiCallCmd.MarkFlagRequired("class"); err != nil { + panic(err) + } + if err := wmiCallCmd.MarkFlagRequired("method"); err != nil { + panic(err) + } } func wmiProcCmdInit() { - wmiProcExecFlags := newFlagSet("Execution") + wmiProcExecFlags := newFlagSet("Execution") - registerExecutionFlags(wmiProcExecFlags.Flags) - registerExecutionOutputFlags(wmiProcExecFlags.Flags) + registerExecutionFlags(wmiProcExecFlags.Flags) + registerExecutionOutputFlags(wmiProcExecFlags.Flags) - wmiProcExecFlags.Flags.StringVarP(&wmiProc.WorkingDirectory, "directory", "d", `C:\`, "Working directory") + wmiProcExecFlags.Flags.StringVarP(&wmiProc.WorkingDirectory, "directory", "d", `C:\`, "Working directory") - cmdFlags[wmiProcCmd] = []*flagSet{ - wmiProcExecFlags, - defaultAuthFlags, - defaultLogFlags, - defaultNetRpcFlags, - } + cmdFlags[wmiProcCmd] = []*flagSet{ + wmiProcExecFlags, + defaultAuthFlags, + defaultLogFlags, + defaultNetRpcFlags, + } - wmiProcCmd.Flags().AddFlagSet(wmiProcExecFlags.Flags) + wmiProcCmd.Flags().AddFlagSet(wmiProcExecFlags.Flags) } var ( - wmiCall = wmiexec.WmiCall{} - wmiProc = wmiexec.WmiProc{} + wmiCall = wmiexec.WmiCall{} + wmiProc = wmiexec.WmiProc{} - wmiArguments string + wmiArguments string - wmiCmd = &cobra.Command{ - Use: "wmi", - Short: "Execute with Windows Management Instrumentation (MS-WMI)", - Long: `Description: + wmiCmd = &cobra.Command{ + Use: "wmi", + Short: "Execute with Windows Management Instrumentation (MS-WMI)", + Long: `Description: The wmi module uses remote Windows Management Instrumentation (WMI) to perform various operations including process creation.`, - GroupID: "module", - Args: cobra.NoArgs, - } + GroupID: "module", + Args: cobra.NoArgs, + } - wmiCallCmd = &cobra.Command{ - Use: "call [target]", - Short: "Execute specified WMI method", - Long: `Description: + wmiCallCmd = &cobra.Command{ + Use: "call [target]", + Short: "Execute specified WMI method", + Long: `Description: The call method creates an instance of the specified WMI class (-c), then calls the provided method (-m) with the provided arguments (-A).`, - Args: args( - argsRpcClient("cifs"), - func(cmd *cobra.Command, args []string) error { - return json.Unmarshal([]byte(wmiArguments), &wmiCall.Args) - }), + Args: args( + argsRpcClient("cifs"), + func(cmd *cobra.Command, args []string) error { + return json.Unmarshal([]byte(wmiArguments), &wmiCall.Args) + }), - Run: func(cmd *cobra.Command, args []string) { - wmiCall.Client = &rpcClient - wmiCall.Out = os.Stdout + Run: func(cmd *cobra.Command, args []string) { + wmiCall.Client = &rpcClient + wmiCall.Out = os.Stdout - ctx := log.With(). - Str("module", "wmi"). - Str("method", "call"). - Logger().WithContext(gssapi.NewSecurityContext(context.Background())) + ctx := log.With(). + Str("module", "wmi"). + Str("method", "call"). + Logger().WithContext(gssapi.NewSecurityContext(context.Background())) - if err := goexec.ExecuteCleanAuxiliaryMethod(ctx, &wmiCall); err != nil { - log.Fatal().Err(err).Msg("Operation failed") - } - }, - } + if err := goexec.ExecuteCleanAuxiliaryMethod(ctx, &wmiCall); err != nil { + log.Fatal().Err(err).Msg("Operation failed") + } + }, + } - wmiProcCmd = &cobra.Command{ - Use: "proc [target]", - Short: "Start a Windows process", - Long: `Description: + wmiProcCmd = &cobra.Command{ + Use: "proc [target]", + Short: "Start a Windows process", + Long: `Description: The proc 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).`, - Args: args( - argsRpcClient("cifs"), - argsOutput("smb"), - ), + Args: args( + argsRpcClient("cifs"), + argsOutput("smb"), + ), - Run: func(cmd *cobra.Command, args []string) { - wmiProc.Client = &rpcClient - wmiProc.IO = exec - wmiProc.Resource = "//./root/cimv2" + Run: func(cmd *cobra.Command, args []string) { + wmiProc.Client = &rpcClient + wmiProc.IO = exec + wmiProc.Resource = "//./root/cimv2" - ctx := log.With(). - Str("module", "wmi"). - Str("method", "proc"). - Logger().WithContext(gssapi.NewSecurityContext(context.Background())) + ctx := log.With(). + Str("module", "wmi"). + Str("method", "proc"). + Logger().WithContext(gssapi.NewSecurityContext(context.Background())) - if err := goexec.ExecuteCleanMethod(ctx, &wmiProc, &exec); err != nil { - log.Fatal().Err(err).Msg("Operation failed") - } - }, - } + if err := goexec.ExecuteCleanMethod(ctx, &wmiProc, &exec); err != nil { + log.Fatal().Err(err).Msg("Operation failed") + } + }, + } ) diff --git a/internal/util/util.go b/internal/util/util.go index d88ec28..5f6c8f3 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -1,41 +1,41 @@ package util import ( - "github.com/google/uuid" - "math/rand" // not crypto secure - "regexp" - "strings" + "github.com/google/uuid" + "math/rand" // not crypto secure + "regexp" + "strings" ) const randHostnameCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-" const randStringCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" var ( - // Up to 15 characters; only letters, digits, and hyphens (with hyphens not at the start or end). - randHostnameRegex = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9-]{0,14}[a-zA-Z0-9]$`) + // Up to 15 characters; only letters, digits, and hyphens (with hyphens not at the start or end). + randHostnameRegex = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9-]{0,14}[a-zA-Z0-9]$`) ) func RandomHostname() (hostname string) { - for { - // between 2 and 10 characters - if hostname = RandomStringFromCharset(randHostnameCharset, rand.Intn(8)+2); randHostnameRegex.MatchString(hostname) { - return - } - } + for { + // between 2 and 10 characters + if hostname = RandomStringFromCharset(randHostnameCharset, rand.Intn(8)+2); randHostnameRegex.MatchString(hostname) { + return + } + } } func RandomWindowsTempFile() string { - return `\Windows\Temp\` + strings.ToUpper(uuid.New().String()) + return `\Windows\Temp\` + strings.ToUpper(uuid.New().String()) } func RandomString() string { - return RandomStringFromCharset(randStringCharset, rand.Intn(10)+6) + return RandomStringFromCharset(randStringCharset, rand.Intn(10)+6) } func RandomStringFromCharset(charset string, length int) string { - b := make([]byte, length) - for i := range length { - b[i] = charset[rand.Intn(len(charset))] - } - return string(b) + b := make([]byte, length) + for i := range length { + b[i] = charset[rand.Intn(len(charset))] + } + return string(b) } diff --git a/main.go b/main.go index 6d0d8ad..d6c84bf 100644 --- a/main.go +++ b/main.go @@ -3,5 +3,5 @@ package main import "github.com/FalconOpsLLC/goexec/cmd" func main() { - cmd.Execute() + cmd.Execute() } diff --git a/pkg/goexec/dcom/dcom.go b/pkg/goexec/dcom/dcom.go index 362c3ba..b926efb 100644 --- a/pkg/goexec/dcom/dcom.go +++ b/pkg/goexec/dcom/dcom.go @@ -1,34 +1,34 @@ package dcomexec import ( - googleUUID "github.com/google/uuid" - "github.com/oiweiwei/go-msrpc/midl/uuid" - "github.com/oiweiwei/go-msrpc/msrpc/dcom" - "github.com/oiweiwei/go-msrpc/msrpc/dtyp" + googleUUID "github.com/google/uuid" + "github.com/oiweiwei/go-msrpc/midl/uuid" + "github.com/oiweiwei/go-msrpc/msrpc/dcom" + "github.com/oiweiwei/go-msrpc/msrpc/dtyp" ) const ( - LcEnglishUs uint32 = 0x409 + LcEnglishUs uint32 = 0x409 ) var ( - ShellBrowserWindowUuid = uuid.MustParse("C08AFD90-F2A1-11D1-8455-00A0C91F3880") - ShellWindowsUuid = uuid.MustParse("9BA05972-F6A8-11CF-A442-00A0C90A8F39") - Mmc20Uuid = uuid.MustParse("49B2791A-B1AE-4C90-9B8E-E860BA07F889") + ShellBrowserWindowUuid = uuid.MustParse("C08AFD90-F2A1-11D1-8455-00A0C91F3880") + ShellWindowsUuid = uuid.MustParse("9BA05972-F6A8-11CF-A442-00A0C90A8F39") + Mmc20Uuid = uuid.MustParse("49B2791A-B1AE-4C90-9B8E-E860BA07F889") - RandCid = dcom.CID(*dtyp.GUIDFromUUID(uuid.MustParse(googleUUID.NewString()))) - IDispatchIID = &dcom.IID{ - Data1: 0x20400, - Data2: 0x0, - Data3: 0x0, - Data4: []byte{0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46}, - } - ComVersion = &dcom.COMVersion{ - MajorVersion: 5, - MinorVersion: 7, - } - ORPCThis = &dcom.ORPCThis{ - Version: ComVersion, - CID: &RandCid, - } + RandCid = dcom.CID(*dtyp.GUIDFromUUID(uuid.MustParse(googleUUID.NewString()))) + IDispatchIID = &dcom.IID{ + Data1: 0x20400, + Data2: 0x0, + Data3: 0x0, + Data4: []byte{0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46}, + } + ComVersion = &dcom.COMVersion{ + MajorVersion: 5, + MinorVersion: 7, + } + ORPCThis = &dcom.ORPCThis{ + Version: ComVersion, + CID: &RandCid, + } ) diff --git a/pkg/goexec/dcom/mmc.go b/pkg/goexec/dcom/mmc.go index e06ce13..97fc4ef 100644 --- a/pkg/goexec/dcom/mmc.go +++ b/pkg/goexec/dcom/mmc.go @@ -1,52 +1,52 @@ package dcomexec import ( - "context" - "fmt" - "github.com/FalconOpsLLC/goexec/pkg/goexec" - "github.com/rs/zerolog" + "context" + "fmt" + "github.com/FalconOpsLLC/goexec/pkg/goexec" + "github.com/rs/zerolog" ) const ( - MethodMmc = "MMC" // MMC20.Application::Document.ActiveView.ExecuteShellCommand + MethodMmc = "MMC" // MMC20.Application::Document.ActiveView.ExecuteShellCommand ) type DcomMmc struct { - Dcom + Dcom - IO goexec.ExecutionIO + IO goexec.ExecutionIO - WorkingDirectory string - WindowState string + WorkingDirectory string + WindowState string } // Execute will perform command execution via the MMC20.Application DCOM object. func (m *DcomMmc) Execute(ctx context.Context, execIO *goexec.ExecutionIO) (err error) { - log := zerolog.Ctx(ctx).With(). - Str("module", ModuleName). - Str("method", MethodMmc). - Logger() + log := zerolog.Ctx(ctx).With(). + Str("module", ModuleName). + Str("method", MethodMmc). + Logger() - method := "Document.ActiveView.ExecuteShellCommand" + method := "Document.ActiveView.ExecuteShellCommand" - cmdline := execIO.CommandLine() - proc := cmdline[0] - args := cmdline[1] + cmdline := execIO.CommandLine() + proc := cmdline[0] + args := cmdline[1] - // Arguments must be passed in reverse order - if _, err := callComMethod(ctx, - m.dispatchClient, - nil, - method, - stringToVariant(m.WindowState), - stringToVariant(args), - stringToVariant(m.WorkingDirectory), - stringToVariant(proc)); err != nil { + // Arguments must be passed in reverse order + if _, err := callComMethod(ctx, + m.dispatchClient, + nil, + method, + stringToVariant(m.WindowState), + stringToVariant(args), + stringToVariant(m.WorkingDirectory), + stringToVariant(proc)); err != nil { - log.Error().Err(err).Msg("Failed to call method") - return fmt.Errorf("call %q: %w", method, err) - } - log.Info().Msg("Method call successful") - return + log.Error().Err(err).Msg("Failed to call method") + return fmt.Errorf("call %q: %w", method, err) + } + log.Info().Msg("Method call successful") + return } diff --git a/pkg/goexec/dcom/module.go b/pkg/goexec/dcom/module.go index d26f3b8..287940c 100644 --- a/pkg/goexec/dcom/module.go +++ b/pkg/goexec/dcom/module.go @@ -1,147 +1,147 @@ package dcomexec import ( - "context" - "errors" - "fmt" - "github.com/FalconOpsLLC/goexec/pkg/goexec" - "github.com/FalconOpsLLC/goexec/pkg/goexec/dce" - "github.com/oiweiwei/go-msrpc/dcerpc" - "github.com/oiweiwei/go-msrpc/midl/uuid" - "github.com/oiweiwei/go-msrpc/msrpc/dcom" - "github.com/oiweiwei/go-msrpc/msrpc/dcom/iremotescmactivator/v0" - "github.com/oiweiwei/go-msrpc/msrpc/dcom/oaut/idispatch/v0" - "github.com/oiweiwei/go-msrpc/msrpc/dtyp" - "github.com/rs/zerolog" + "context" + "errors" + "fmt" + "github.com/FalconOpsLLC/goexec/pkg/goexec" + "github.com/FalconOpsLLC/goexec/pkg/goexec/dce" + "github.com/oiweiwei/go-msrpc/dcerpc" + "github.com/oiweiwei/go-msrpc/midl/uuid" + "github.com/oiweiwei/go-msrpc/msrpc/dcom" + "github.com/oiweiwei/go-msrpc/msrpc/dcom/iremotescmactivator/v0" + "github.com/oiweiwei/go-msrpc/msrpc/dcom/oaut/idispatch/v0" + "github.com/oiweiwei/go-msrpc/msrpc/dtyp" + "github.com/rs/zerolog" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32" ) const ( - ModuleName = "DCOM" + ModuleName = "DCOM" ) type Dcom struct { - goexec.Cleaner - goexec.Executor + goexec.Cleaner + goexec.Executor - Client *dce.Client - ClassID *uuid.UUID + Client *dce.Client + ClassID *uuid.UUID - dispatchClient idispatch.DispatchClient + dispatchClient idispatch.DispatchClient } func (m *Dcom) Connect(ctx context.Context) (err error) { - if err = m.Client.Connect(ctx); err == nil { - m.AddCleaners(m.Client.Close) - } - return + if err = m.Client.Connect(ctx); err == nil { + m.AddCleaners(m.Client.Close) + } + return } func (m *Dcom) Init(ctx context.Context) (err error) { - log := zerolog.Ctx(ctx).With(). - Str("module", ModuleName).Logger() + log := zerolog.Ctx(ctx).With(). + Str("module", ModuleName).Logger() - if m.Client == nil || m.Client.Dce() == nil { - return errors.New("DCE connection not initialized") - } + if m.Client == nil || m.Client.Dce() == nil { + return errors.New("DCE connection not initialized") + } - if m.ClassID == nil { - return errors.New("CLSID not specified") - } + if m.ClassID == nil { + return errors.New("CLSID not specified") + } - class := dcom.ClassID(*dtyp.GUIDFromUUID(m.ClassID)) + class := dcom.ClassID(*dtyp.GUIDFromUUID(m.ClassID)) - if class.GUID() == nil { - return fmt.Errorf("invalid class ID: %s", m.ClassID) - } + if class.GUID() == nil { + return fmt.Errorf("invalid class ID: %s", m.ClassID) + } - opts := []dcerpc.Option{ - dcerpc.WithSign(), - } + opts := []dcerpc.Option{ + dcerpc.WithSign(), + } - inst := &dcom.InstantiationInfoData{ - ClassID: &class, - IID: []*dcom.IID{IDispatchIID}, - ClientCOMVersion: ComVersion, - } - ac := &dcom.ActivationContextInfoData{} - loc := &dcom.LocationInfoData{} - scm := &dcom.SCMRequestInfoData{ - RemoteRequest: &dcom.CustomRemoteRequestSCMInfo{ - RequestedProtocolSequences: []uint16{7}, - }, - } + inst := &dcom.InstantiationInfoData{ + ClassID: &class, + IID: []*dcom.IID{IDispatchIID}, + ClientCOMVersion: ComVersion, + } + ac := &dcom.ActivationContextInfoData{} + loc := &dcom.LocationInfoData{} + scm := &dcom.SCMRequestInfoData{ + RemoteRequest: &dcom.CustomRemoteRequestSCMInfo{ + RequestedProtocolSequences: []uint16{7}, + }, + } - ap := &dcom.ActivationProperties{ - DestinationContext: 2, - Properties: []dcom.ActivationProperty{inst, ac, loc, scm}, - } + ap := &dcom.ActivationProperties{ + DestinationContext: 2, + Properties: []dcom.ActivationProperty{inst, ac, loc, scm}, + } - apin, err := ap.ActivationPropertiesIn() - if err != nil { - return err - } + apin, err := ap.ActivationPropertiesIn() + if err != nil { + return err + } - act, err := iremotescmactivator.NewRemoteSCMActivatorClient(ctx, m.Client.Dce()) - if err != nil { - return err - } + act, err := iremotescmactivator.NewRemoteSCMActivatorClient(ctx, m.Client.Dce()) + if err != nil { + return err + } - cr, err := act.RemoteCreateInstance(ctx, &iremotescmactivator.RemoteCreateInstanceRequest{ - ORPCThis: &dcom.ORPCThis{ - Version: ComVersion, - Flags: 1, - CID: &RandCid, - }, - ActPropertiesIn: apin, - }) - if err != nil { - return err - } - log.Info().Msg("RemoteCreateInstance succeeded") + cr, err := act.RemoteCreateInstance(ctx, &iremotescmactivator.RemoteCreateInstanceRequest{ + ORPCThis: &dcom.ORPCThis{ + Version: ComVersion, + Flags: 1, + CID: &RandCid, + }, + ActPropertiesIn: apin, + }) + if err != nil { + return err + } + log.Info().Msg("RemoteCreateInstance succeeded") - apout := new(dcom.ActivationProperties) - if err = apout.Parse(cr.ActPropertiesOut); err != nil { - return err - } - si := apout.SCMReplyInfoData() - pi := apout.PropertiesOutInfo() + apout := new(dcom.ActivationProperties) + if err = apout.Parse(cr.ActPropertiesOut); err != nil { + return err + } + si := apout.SCMReplyInfoData() + pi := apout.PropertiesOutInfo() - if si == nil { - return fmt.Errorf("remote create instance response: SCMReplyInfoData is nil") - } + if si == nil { + return fmt.Errorf("remote create instance response: SCMReplyInfoData is nil") + } - if pi == nil { - return fmt.Errorf("remote create instance response: PropertiesOutInfo is nil") - } + if pi == nil { + return fmt.Errorf("remote create instance response: PropertiesOutInfo is nil") + } - // Ensure that the string bindings don't contain the target hostname - for _, bind := range si.RemoteReply.OXIDBindings.GetStringBindings() { - stringBinding, err := dcerpc.ParseStringBinding("ncacn_ip_tcp:" + bind.NetworkAddr) // TODO: try bind.String() + // Ensure that the string bindings don't contain the target hostname + for _, bind := range si.RemoteReply.OXIDBindings.GetStringBindings() { + stringBinding, err := dcerpc.ParseStringBinding("ncacn_ip_tcp:" + bind.NetworkAddr) // TODO: try bind.String() - if err != nil { - log.Debug().Err(err).Msg("Failed to parse string binding") - continue - } - stringBinding.NetworkAddress = "" - opts = append(opts, dcerpc.WithEndpoint(stringBinding.String())) - } + if err != nil { + log.Debug().Err(err).Msg("Failed to parse string binding") + continue + } + stringBinding.NetworkAddress = "" + opts = append(opts, dcerpc.WithEndpoint(stringBinding.String())) + } - err = m.Client.Reconnect(ctx, opts...) - if err != nil { - return err - } - log.Info().Msg("created new DCERPC dialer") + err = m.Client.Reconnect(ctx, opts...) + if err != nil { + return err + } + log.Info().Msg("created new DCERPC dialer") - m.dispatchClient, err = idispatch.NewDispatchClient(ctx, m.Client.Dce(), dcom.WithIPID(pi.InterfaceData[0].IPID())) - if err != nil { - return err - } - log.Info().Msg("created IDispatch Client") + m.dispatchClient, err = idispatch.NewDispatchClient(ctx, m.Client.Dce(), dcom.WithIPID(pi.InterfaceData[0].IPID())) + if err != nil { + return err + } + log.Info().Msg("created IDispatch Client") - return + return } diff --git a/pkg/goexec/dcom/shellbrowserwindow.go b/pkg/goexec/dcom/shellbrowserwindow.go index 4d1bcf7..ef2500a 100644 --- a/pkg/goexec/dcom/shellbrowserwindow.go +++ b/pkg/goexec/dcom/shellbrowserwindow.go @@ -1,52 +1,52 @@ package dcomexec import ( - "context" - "fmt" - "github.com/FalconOpsLLC/goexec/pkg/goexec" - "github.com/rs/zerolog" + "context" + "fmt" + "github.com/FalconOpsLLC/goexec/pkg/goexec" + "github.com/rs/zerolog" ) const ( - MethodShellBrowserWindow = "ShellBrowserWindow" // ShellBrowserWindow::Document.Application.ShellExecute + MethodShellBrowserWindow = "ShellBrowserWindow" // ShellBrowserWindow::Document.Application.ShellExecute ) type DcomShellBrowserWindow struct { - Dcom + Dcom - IO goexec.ExecutionIO + IO goexec.ExecutionIO - WorkingDirectory string - WindowState string + WorkingDirectory string + WindowState string } // Execute will perform command execution via the ShellBrowserWindow object. See https://enigma0x3.net/2017/01/23/lateral-movement-via-dcom-round-2/ func (m *DcomShellBrowserWindow) Execute(ctx context.Context, execIO *goexec.ExecutionIO) (err error) { - log := zerolog.Ctx(ctx).With(). - Str("module", ModuleName). - Str("method", MethodShellBrowserWindow). - Logger() + log := zerolog.Ctx(ctx).With(). + Str("module", ModuleName). + Str("method", MethodShellBrowserWindow). + Logger() - method := "Document.Application.ShellExecute" + method := "Document.Application.ShellExecute" - cmdline := execIO.CommandLine() - proc := cmdline[0] - args := cmdline[1] + cmdline := execIO.CommandLine() + proc := cmdline[0] + args := cmdline[1] - // Arguments must be passed in reverse order - if _, err := callComMethod(ctx, m.dispatchClient, - nil, - method, - stringToVariant(m.WindowState), - stringToVariant(""), // FUTURE? - stringToVariant(m.WorkingDirectory), - stringToVariant(args), - stringToVariant(proc)); err != nil { + // Arguments must be passed in reverse order + if _, err := callComMethod(ctx, m.dispatchClient, + nil, + method, + stringToVariant(m.WindowState), + stringToVariant(""), // FUTURE? + stringToVariant(m.WorkingDirectory), + stringToVariant(args), + stringToVariant(proc)); err != nil { - log.Error().Err(err).Msg("Failed to call method") - return fmt.Errorf("call %q: %w", method, err) - } - log.Info().Msg("Method call successful") - return + log.Error().Err(err).Msg("Failed to call method") + return fmt.Errorf("call %q: %w", method, err) + } + log.Info().Msg("Method call successful") + return } diff --git a/pkg/goexec/dcom/shellwindows.go b/pkg/goexec/dcom/shellwindows.go index 67537ec..aa8417f 100644 --- a/pkg/goexec/dcom/shellwindows.go +++ b/pkg/goexec/dcom/shellwindows.go @@ -1,73 +1,73 @@ package dcomexec import ( - "context" - "errors" - "fmt" - "github.com/FalconOpsLLC/goexec/pkg/goexec" - "github.com/oiweiwei/go-msrpc/msrpc/dcom/oaut" - "github.com/rs/zerolog" + "context" + "errors" + "fmt" + "github.com/FalconOpsLLC/goexec/pkg/goexec" + "github.com/oiweiwei/go-msrpc/msrpc/dcom/oaut" + "github.com/rs/zerolog" ) const ( - MethodShellWindows = "ShellWindows" // ShellWindows::Item().Document.Application.ShellExecute + MethodShellWindows = "ShellWindows" // ShellWindows::Item().Document.Application.ShellExecute ) type DcomShellWindows struct { - Dcom + Dcom - IO goexec.ExecutionIO + IO goexec.ExecutionIO - WorkingDirectory string - WindowState string + WorkingDirectory string + WindowState string } // Execute will perform command execution via the ShellWindows object. See https://enigma0x3.net/2017/01/23/lateral-movement-via-dcom-round-2/ func (m *DcomShellWindows) Execute(ctx context.Context, execIO *goexec.ExecutionIO) (err error) { - log := zerolog.Ctx(ctx).With(). - Str("module", ModuleName). - Str("method", MethodShellWindows). - Logger() + log := zerolog.Ctx(ctx).With(). + Str("module", ModuleName). + Str("method", MethodShellWindows). + Logger() - method := "Item" + method := "Item" - cmdline := execIO.CommandLine() - proc := cmdline[0] - args := cmdline[1] + cmdline := execIO.CommandLine() + proc := cmdline[0] + args := cmdline[1] - iv, err := callComMethod(ctx, - m.dispatchClient, - nil, - "Item") + iv, err := callComMethod(ctx, + m.dispatchClient, + nil, + "Item") - if err != nil { - log.Error().Err(err).Msg("Failed to call method") - return fmt.Errorf("call method %q: %w", method, err) - } + if err != nil { + log.Error().Err(err).Msg("Failed to call method") + return fmt.Errorf("call method %q: %w", method, err) + } - item, ok := iv.VarResult.VarUnion.GetValue().(*oaut.Dispatch) - if !ok { - return errors.New("failed to get dispatch from ShellWindows::Item()") - } + item, ok := iv.VarResult.VarUnion.GetValue().(*oaut.Dispatch) + if !ok { + return errors.New("failed to get dispatch from ShellWindows::Item()") + } - method = "Document.Application.ShellExecute" + method = "Document.Application.ShellExecute" - // Arguments must be passed in reverse order - if _, err := callComMethod(ctx, m.dispatchClient, - item.InterfacePointer(). - GetStandardObjectReference(). - Std.IPID, - method, - stringToVariant(m.WindowState), - stringToVariant(""), // FUTURE? - stringToVariant(m.WorkingDirectory), - stringToVariant(args), - stringToVariant(proc)); err != nil { + // Arguments must be passed in reverse order + if _, err := callComMethod(ctx, m.dispatchClient, + item.InterfacePointer(). + GetStandardObjectReference(). + Std.IPID, + method, + stringToVariant(m.WindowState), + stringToVariant(""), // FUTURE? + stringToVariant(m.WorkingDirectory), + stringToVariant(args), + stringToVariant(proc)); err != nil { - log.Error().Err(err).Msg("Failed to call method") - return fmt.Errorf("call %q: %w", method, err) - } - log.Info().Msg("Method call successful") - return + log.Error().Err(err).Msg("Failed to call method") + return fmt.Errorf("call %q: %w", method, err) + } + log.Info().Msg("Method call successful") + return } diff --git a/pkg/goexec/dcom/util.go b/pkg/goexec/dcom/util.go index 8389a73..b19e841 100644 --- a/pkg/goexec/dcom/util.go +++ b/pkg/goexec/dcom/util.go @@ -1,85 +1,85 @@ package dcomexec import ( - "context" - "fmt" - "github.com/oiweiwei/go-msrpc/dcerpc" - "github.com/oiweiwei/go-msrpc/msrpc/dcom" - "github.com/oiweiwei/go-msrpc/msrpc/dcom/oaut" - "github.com/oiweiwei/go-msrpc/msrpc/dcom/oaut/idispatch/v0" - "strings" + "context" + "fmt" + "github.com/oiweiwei/go-msrpc/dcerpc" + "github.com/oiweiwei/go-msrpc/msrpc/dcom" + "github.com/oiweiwei/go-msrpc/msrpc/dcom/oaut" + "github.com/oiweiwei/go-msrpc/msrpc/dcom/oaut/idispatch/v0" + "strings" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32" ) func callComMethod(ctx context.Context, dc idispatch.DispatchClient, id *dcom.IPID, method string, args ...*oaut.Variant) (ir *idispatch.InvokeResponse, err error) { - parts := strings.Split(method, ".") + parts := strings.Split(method, ".") - for i, obj := range parts { + for i, obj := range parts { - var opts []dcerpc.CallOption + var opts []dcerpc.CallOption - if id != nil { - opts = append(opts, dcom.WithIPID(id)) - } + if id != nil { + opts = append(opts, dcom.WithIPID(id)) + } - gr, err := dc.GetIDsOfNames(ctx, &idispatch.GetIDsOfNamesRequest{ - This: ORPCThis, - IID: &dcom.IID{}, - LocaleID: LcEnglishUs, + gr, err := dc.GetIDsOfNames(ctx, &idispatch.GetIDsOfNamesRequest{ + This: ORPCThis, + IID: &dcom.IID{}, + LocaleID: LcEnglishUs, - Names: []string{obj + "\x00"}, - }, opts...) + Names: []string{obj + "\x00"}, + }, opts...) - if err != nil { - return nil, fmt.Errorf("get dispatch ID of name %q: %w", obj, err) - } + if err != nil { + return nil, fmt.Errorf("get dispatch ID of name %q: %w", obj, err) + } - if len(gr.DispatchID) < 1 { - return nil, fmt.Errorf("dispatch ID of name %q not found", obj) - } + if len(gr.DispatchID) < 1 { + return nil, fmt.Errorf("dispatch ID of name %q not found", obj) + } - irq := &idispatch.InvokeRequest{ - This: ORPCThis, - IID: &dcom.IID{}, - LocaleID: LcEnglishUs, + irq := &idispatch.InvokeRequest{ + This: ORPCThis, + IID: &dcom.IID{}, + LocaleID: LcEnglishUs, - DispatchIDMember: gr.DispatchID[0], - } + DispatchIDMember: gr.DispatchID[0], + } - if i >= len(parts)-1 { - irq.Flags = 1 - irq.DispatchParams = &oaut.DispatchParams{ArgsCount: uint32(len(args)), Args: args} - return dc.Invoke(ctx, irq, opts...) - } - irq.Flags = 2 + if i >= len(parts)-1 { + irq.Flags = 1 + irq.DispatchParams = &oaut.DispatchParams{ArgsCount: uint32(len(args)), Args: args} + return dc.Invoke(ctx, irq, opts...) + } + irq.Flags = 2 - ir, err = dc.Invoke(ctx, irq, opts...) - if err != nil { - return nil, fmt.Errorf("get properties of object %q: %w", obj, err) - } + ir, err = dc.Invoke(ctx, irq, opts...) + if err != nil { + return nil, fmt.Errorf("get properties of object %q: %w", obj, err) + } - di, ok := ir.VarResult.VarUnion.GetValue().(*oaut.Dispatch) - if !ok { - return nil, fmt.Errorf("invalid dispatch object for %q", obj) - } - id = di.InterfacePointer().GetStandardObjectReference().Std.IPID - } - return + di, ok := ir.VarResult.VarUnion.GetValue().(*oaut.Dispatch) + if !ok { + return nil, fmt.Errorf("invalid dispatch object for %q", obj) + } + id = di.InterfacePointer().GetStandardObjectReference().Std.IPID + } + return } func stringToVariant(s string) *oaut.Variant { - return &oaut.Variant{ - Size: 5, - VT: 8, - VarUnion: &oaut.Variant_VarUnion{ - Value: &oaut.Variant_VarUnion_BSTR{ - BSTR: &oaut.String{ - Data: s, - }, - }, - }, - } + return &oaut.Variant{ + Size: 5, + VT: 8, + VarUnion: &oaut.Variant_VarUnion{ + Value: &oaut.Variant_VarUnion_BSTR{ + BSTR: &oaut.String{ + Data: s, + }, + }, + }, + } } diff --git a/pkg/goexec/io.go b/pkg/goexec/io.go index c8ad417..c5a7dd8 100644 --- a/pkg/goexec/io.go +++ b/pkg/goexec/io.go @@ -1,99 +1,99 @@ package goexec import ( - "context" - "fmt" - "io" - "strings" + "context" + "fmt" + "io" + "strings" ) type OutputProvider interface { - GetOutput(ctx context.Context, writer io.Writer) (err error) - Clean(ctx context.Context) (err error) + GetOutput(ctx context.Context, writer io.Writer) (err error) + Clean(ctx context.Context) (err error) } type ExecutionIO struct { - Cleaner + Cleaner - Input *ExecutionInput - Output *ExecutionOutput + Input *ExecutionInput + Output *ExecutionOutput } type ExecutionOutput struct { - NoDelete bool - RemotePath string - Provider OutputProvider - Writer io.WriteCloser + NoDelete bool + RemotePath string + Provider OutputProvider + Writer io.WriteCloser } type ExecutionInput struct { - StageFile io.ReadCloser - Executable string - ExecutablePath string - Arguments string - Command string + StageFile io.ReadCloser + Executable string + ExecutablePath string + Arguments string + Command string } func (execIO *ExecutionIO) GetOutput(ctx context.Context) (err error) { - if execIO.Output.Provider != nil { - return execIO.Output.Provider.GetOutput(ctx, execIO.Output.Writer) - } - return nil + if execIO.Output.Provider != nil { + return execIO.Output.Provider.GetOutput(ctx, execIO.Output.Writer) + } + return nil } func (execIO *ExecutionIO) Clean(ctx context.Context) (err error) { - if execIO.Output.Provider != nil { - return execIO.Output.Provider.Clean(ctx) - } - return nil + if execIO.Output.Provider != nil { + return execIO.Output.Provider.Clean(ctx) + } + return nil } func (execIO *ExecutionIO) CommandLine() (cmd []string) { - if execIO.Output.Provider != nil && execIO.Output.RemotePath != "" { - return []string{ - `C:\Windows\System32\cmd.exe`, - fmt.Sprintf(`/C %s > %s 2>&1`, execIO.Input.String(), execIO.Output.RemotePath), - } - } - return execIO.Input.CommandLine() + if execIO.Output.Provider != nil && execIO.Output.RemotePath != "" { + return []string{ + `C:\Windows\System32\cmd.exe`, + fmt.Sprintf(`/C %s > %s 2>&1`, execIO.Input.String(), execIO.Output.RemotePath), + } + } + return execIO.Input.CommandLine() } func (execIO *ExecutionIO) String() (str string) { - cmd := execIO.CommandLine() - // Ensure that executable paths are quoted - if strings.Contains(cmd[0], " ") { - str = fmt.Sprintf(`%q %s`, cmd[0], strings.Join(cmd[1:], " ")) - } else { - str = strings.Join(cmd, " ") - } - return strings.Trim(str, " \t\n\r") // trim whitespace + cmd := execIO.CommandLine() + // Ensure that executable paths are quoted + if strings.Contains(cmd[0], " ") { + str = fmt.Sprintf(`%q %s`, cmd[0], strings.Join(cmd[1:], " ")) + } else { + str = strings.Join(cmd, " ") + } + return strings.Trim(str, " \t\n\r") // trim whitespace } func (i *ExecutionInput) CommandLine() (cmd []string) { - cmd = make([]string, 2) - cmd[1] = i.Arguments + cmd = make([]string, 2) + cmd[1] = i.Arguments - switch { - case i.Command != "": - return strings.SplitN(i.Command, " ", 2) + switch { + case i.Command != "": + return strings.SplitN(i.Command, " ", 2) - case i.ExecutablePath != "": - cmd[0] = i.ExecutablePath + case i.ExecutablePath != "": + cmd[0] = i.ExecutablePath - case i.Executable != "": - cmd[0] = i.Executable - } + case i.Executable != "": + cmd[0] = i.Executable + } - return cmd + return cmd } func (i *ExecutionInput) String() string { - return strings.Join(i.CommandLine(), " ") + return strings.Join(i.CommandLine(), " ") } func (i *ExecutionInput) Reader() (reader io.Reader) { - if i.StageFile != nil { - return i.StageFile - } - return strings.NewReader(i.String()) + if i.StageFile != nil { + return i.StageFile + } + return strings.NewReader(i.String()) } diff --git a/pkg/goexec/scmr/change.go b/pkg/goexec/scmr/change.go index eaf4c25..c5fd389 100644 --- a/pkg/goexec/scmr/change.go +++ b/pkg/goexec/scmr/change.go @@ -1,154 +1,154 @@ package scmrexec import ( - "context" - "fmt" - "github.com/FalconOpsLLC/goexec/pkg/goexec" - "github.com/oiweiwei/go-msrpc/msrpc/scmr/svcctl/v2" - "github.com/rs/zerolog" + "context" + "fmt" + "github.com/FalconOpsLLC/goexec/pkg/goexec" + "github.com/oiweiwei/go-msrpc/msrpc/scmr/svcctl/v2" + "github.com/rs/zerolog" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32" ) const ( - MethodChange = "Change" + MethodChange = "Change" ) type ScmrChange struct { - Scmr - goexec.Cleaner - goexec.Executor + Scmr + goexec.Cleaner + goexec.Executor - IO goexec.ExecutionIO + IO goexec.ExecutionIO - NoStart bool - NoRevert bool - ServiceName string + NoStart bool + NoRevert bool + ServiceName string } func (m *ScmrChange) Execute(ctx context.Context, in *goexec.ExecutionIO) (err error) { - log := zerolog.Ctx(ctx).With(). - Str("service", m.ServiceName). - Logger() + log := zerolog.Ctx(ctx).With(). + Str("service", m.ServiceName). + Logger() - svc := &service{name: m.ServiceName} + svc := &service{name: m.ServiceName} - openResponse, err := m.ctl.OpenServiceW(ctx, &svcctl.OpenServiceWRequest{ - ServiceManager: m.scm, - ServiceName: svc.name, - DesiredAccess: ServiceAllAccess, - }) + openResponse, err := m.ctl.OpenServiceW(ctx, &svcctl.OpenServiceWRequest{ + ServiceManager: m.scm, + ServiceName: svc.name, + DesiredAccess: ServiceAllAccess, + }) - if err != nil { - log.Error().Err(err).Msg("Failed to open service handle") - return fmt.Errorf("open service request: %w", err) - } - if openResponse.Return != 0 { - log.Error().Err(err).Msg("Failed to open service handle") - return fmt.Errorf("create service: %w", err) - } + if err != nil { + log.Error().Err(err).Msg("Failed to open service handle") + return fmt.Errorf("open service request: %w", err) + } + if openResponse.Return != 0 { + log.Error().Err(err).Msg("Failed to open service handle") + return fmt.Errorf("create service: %w", err) + } - svc.handle = openResponse.Service - log.Info().Msg("Opened service handle") + svc.handle = openResponse.Service + log.Info().Msg("Opened service handle") - defer m.AddCleaners(func(ctxInner context.Context) error { - return m.closeService(ctxInner, svc) - }) + defer m.AddCleaners(func(ctxInner context.Context) error { + return m.closeService(ctxInner, svc) + }) - // Note the original service configuration - queryResponse, err := m.ctl.QueryServiceConfigW(ctx, &svcctl.QueryServiceConfigWRequest{ - Service: svc.handle, - BufferLength: 8 * 1024, - }) + // Note the original service configuration + queryResponse, err := m.ctl.QueryServiceConfigW(ctx, &svcctl.QueryServiceConfigWRequest{ + Service: svc.handle, + BufferLength: 8 * 1024, + }) - if err != nil { - log.Error().Err(err).Msg("Failed to fetch service configuration") - return fmt.Errorf("get service config: %w", err) - } + if err != nil { + log.Error().Err(err).Msg("Failed to fetch service configuration") + return fmt.Errorf("get service config: %w", err) + } - log.Info().Str("binaryPath", queryResponse.ServiceConfig.BinaryPathName).Msg("Fetched original service configuration") - svc.originalConfig = queryResponse.ServiceConfig + log.Info().Str("binaryPath", queryResponse.ServiceConfig.BinaryPathName).Msg("Fetched original service configuration") + svc.originalConfig = queryResponse.ServiceConfig - stopResponse, err := m.ctl.ControlService(ctx, &svcctl.ControlServiceRequest{ - Service: svc.handle, - Control: ServiceControlStop, - }) + stopResponse, err := m.ctl.ControlService(ctx, &svcctl.ControlServiceRequest{ + Service: svc.handle, + Control: ServiceControlStop, + }) - if err != nil { - if stopResponse == nil || stopResponse.Return != ErrorServiceNotActive { + if err != nil { + if stopResponse == nil || stopResponse.Return != ErrorServiceNotActive { - log.Error().Err(err).Msg("Failed to stop existing service") - return fmt.Errorf("stop service: %w", err) - } + log.Error().Err(err).Msg("Failed to stop existing service") + return fmt.Errorf("stop service: %w", err) + } - log.Debug().Msg("Service is not running") + log.Debug().Msg("Service is not running") - // FEATURE: restore state - /* - defer m.AddCleaners(func(ctxInner context.Context) error { - // ... - return nil - }) - */ + // FEATURE: restore state + /* + defer m.AddCleaners(func(ctxInner context.Context) error { + // ... + return nil + }) + */ - } else { - log.Info().Msg("Stopped existing service") - } + } else { + log.Info().Msg("Stopped existing service") + } - req := &svcctl.ChangeServiceConfigWRequest{ - Service: svc.handle, - BinaryPathName: in.String(), - DisplayName: svc.originalConfig.DisplayName, - ServiceType: svc.originalConfig.ServiceType, - StartType: ServiceDemandStart, - ErrorControl: svc.originalConfig.ErrorControl, - LoadOrderGroup: svc.originalConfig.LoadOrderGroup, - ServiceStartName: svc.originalConfig.ServiceStartName, - TagID: svc.originalConfig.TagID, - Dependencies: parseDependencies(svc.originalConfig.Dependencies), - } + req := &svcctl.ChangeServiceConfigWRequest{ + Service: svc.handle, + BinaryPathName: in.String(), + DisplayName: svc.originalConfig.DisplayName, + ServiceType: svc.originalConfig.ServiceType, + StartType: ServiceDemandStart, + ErrorControl: svc.originalConfig.ErrorControl, + LoadOrderGroup: svc.originalConfig.LoadOrderGroup, + ServiceStartName: svc.originalConfig.ServiceStartName, + TagID: svc.originalConfig.TagID, + Dependencies: parseDependencies(svc.originalConfig.Dependencies), + } - bpn := svc.originalConfig.BinaryPathName + bpn := svc.originalConfig.BinaryPathName - _, err = m.ctl.ChangeServiceConfigW(ctx, req) + _, err = m.ctl.ChangeServiceConfigW(ctx, req) - if err != nil { - log.Error().Err(err).Msg("Failed to request service configuration change") - return fmt.Errorf("change service config request: %w", err) - } + if err != nil { + log.Error().Err(err).Msg("Failed to request service configuration change") + return fmt.Errorf("change service config request: %w", err) + } - if !m.NoStart { - err = m.startService(ctx, svc) - if err != nil { - log.Error().Err(err).Msg("Failed to start service") - } - } + if !m.NoStart { + err = m.startService(ctx, svc) + if err != nil { + log.Error().Err(err).Msg("Failed to start service") + } + } - if !m.NoRevert { - if svc.handle == nil { + if !m.NoRevert { + if svc.handle == nil { - if err = m.Reconnect(ctx); err != nil { - return err - } - svc, err = m.openService(ctx, svc.name) + if err = m.Reconnect(ctx); err != nil { + return err + } + svc, err = m.openService(ctx, svc.name) - if err != nil { - log.Error().Err(err).Msg("Failed to reopen service handle") - return fmt.Errorf("reopen service: %w", err) - } - } - req.BinaryPathName = bpn - req.Service = svc.handle - _, err := m.ctl.ChangeServiceConfigW(ctx, req) + if err != nil { + log.Error().Err(err).Msg("Failed to reopen service handle") + return fmt.Errorf("reopen service: %w", err) + } + } + req.BinaryPathName = bpn + req.Service = svc.handle + _, err := m.ctl.ChangeServiceConfigW(ctx, req) - if err != nil { - log.Error().Err(err).Msg("Failed to restore original service configuration") - return fmt.Errorf("restore service config: %w", err) - } - log.Info().Msg("Restored original service configuration") - } + if err != nil { + log.Error().Err(err).Msg("Failed to restore original service configuration") + return fmt.Errorf("restore service config: %w", err) + } + log.Info().Msg("Restored original service configuration") + } - return + return } diff --git a/pkg/goexec/scmr/create.go b/pkg/goexec/scmr/create.go index f0245af..9b0a572 100644 --- a/pkg/goexec/scmr/create.go +++ b/pkg/goexec/scmr/create.go @@ -1,129 +1,129 @@ package scmrexec import ( - "context" - "fmt" - "github.com/FalconOpsLLC/goexec/internal/util" - "github.com/FalconOpsLLC/goexec/pkg/goexec" - "github.com/oiweiwei/go-msrpc/msrpc/scmr/svcctl/v2" - "github.com/rs/zerolog" + "context" + "fmt" + "github.com/FalconOpsLLC/goexec/internal/util" + "github.com/FalconOpsLLC/goexec/pkg/goexec" + "github.com/oiweiwei/go-msrpc/msrpc/scmr/svcctl/v2" + "github.com/rs/zerolog" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32" ) const ( - MethodCreate = "Create" + MethodCreate = "Create" ) type ScmrCreate struct { - Scmr - goexec.Cleaner - goexec.Executor + Scmr + goexec.Cleaner + goexec.Executor - IO goexec.ExecutionIO + IO goexec.ExecutionIO - NoDelete bool - NoStart bool - ServiceName string - DisplayName string + NoDelete bool + NoStart bool + ServiceName string + DisplayName string } func (m *ScmrCreate) ensure() { - if m.ServiceName == "" { - m.ServiceName = util.RandomString() - } - if m.DisplayName == "" { - m.DisplayName = m.ServiceName - } + if m.ServiceName == "" { + m.ServiceName = util.RandomString() + } + if m.DisplayName == "" { + m.DisplayName = m.ServiceName + } } func (m *ScmrCreate) Execute(ctx context.Context, in *goexec.ExecutionIO) (err error) { - m.ensure() + m.ensure() - log := zerolog.Ctx(ctx).With(). - Str("service", m.ServiceName).Logger() + log := zerolog.Ctx(ctx).With(). + Str("service", m.ServiceName).Logger() - svc := &service{name: m.ServiceName} + svc := &service{name: m.ServiceName} - resp, err := m.ctl.CreateServiceW(ctx, &svcctl.CreateServiceWRequest{ - ServiceManager: m.scm, - ServiceName: m.ServiceName, - DisplayName: m.DisplayName, - BinaryPathName: in.String(), - ServiceType: ServiceWin32OwnProcess, - StartType: ServiceDemandStart, - DesiredAccess: ServiceAllAccess, // TODO: Replace - }) + resp, err := m.ctl.CreateServiceW(ctx, &svcctl.CreateServiceWRequest{ + ServiceManager: m.scm, + ServiceName: m.ServiceName, + DisplayName: m.DisplayName, + BinaryPathName: in.String(), + ServiceType: ServiceWin32OwnProcess, + StartType: ServiceDemandStart, + DesiredAccess: ServiceAllAccess, // TODO: Replace + }) - if err != nil { - log.Error().Err(err).Msg("Create service request failed") - return fmt.Errorf("create service request: %w", err) - } + if err != nil { + log.Error().Err(err).Msg("Create service request failed") + return fmt.Errorf("create service request: %w", err) + } - if resp.Return != 0 { - log.Error().Err(err).Msg("Failed to create service") - return fmt.Errorf("create service returned non-zero exit code: %02x", resp.Return) - } + if resp.Return != 0 { + log.Error().Err(err).Msg("Failed to create service") + return fmt.Errorf("create service returned non-zero exit code: %02x", resp.Return) + } - if !m.NoDelete { - m.AddCleaners(func(ctxInner context.Context) error { + if !m.NoDelete { + m.AddCleaners(func(ctxInner context.Context) error { - r, errInner := m.ctl.DeleteService(ctxInner, &svcctl.DeleteServiceRequest{ - Service: svc.handle, - }) - if errInner != nil { - return fmt.Errorf("delete service: %w", errInner) - } - if r.Return != 0 { - return fmt.Errorf("delete service returned non-zero exit code: %02x", r.Return) - } - log.Info().Msg("Deleted service") + r, errInner := m.ctl.DeleteService(ctxInner, &svcctl.DeleteServiceRequest{ + Service: svc.handle, + }) + if errInner != nil { + return fmt.Errorf("delete service: %w", errInner) + } + if r.Return != 0 { + return fmt.Errorf("delete service returned non-zero exit code: %02x", r.Return) + } + log.Info().Msg("Deleted service") - return nil - }) - } + return nil + }) + } - m.AddCleaners(func(ctxInner context.Context) error { + m.AddCleaners(func(ctxInner context.Context) error { - r, errInner := m.ctl.CloseService(ctxInner, &svcctl.CloseServiceRequest{ - ServiceObject: svc.handle, - }) - if errInner != nil { - return fmt.Errorf("close service: %w", errInner) - } - if r.Return != 0 { - return fmt.Errorf("close service returned non-zero exit code: %02x", r.Return) - } - log.Info().Msg("Closed service handle") + r, errInner := m.ctl.CloseService(ctxInner, &svcctl.CloseServiceRequest{ + ServiceObject: svc.handle, + }) + if errInner != nil { + return fmt.Errorf("close service: %w", errInner) + } + if r.Return != 0 { + return fmt.Errorf("close service returned non-zero exit code: %02x", r.Return) + } + log.Info().Msg("Closed service handle") - return nil - }) + return nil + }) - log.Info().Msg("Created service") - svc.handle = resp.Service + log.Info().Msg("Created service") + svc.handle = resp.Service - if !m.NoStart { + if !m.NoStart { - err = m.startService(ctx, svc) + err = m.startService(ctx, svc) - if err != nil { - log.Error().Err(err).Msg("Failed to start service") - return fmt.Errorf("start service: %w", err) - } - } - if svc.handle == nil { + if err != nil { + log.Error().Err(err).Msg("Failed to start service") + return fmt.Errorf("start service: %w", err) + } + } + if svc.handle == nil { - if err = m.Reconnect(ctx); err != nil { - return err - } - svc, err = m.openService(ctx, svc.name) + if err = m.Reconnect(ctx); err != nil { + return err + } + svc, err = m.openService(ctx, svc.name) - if err != nil { - log.Error().Err(err).Msg("Failed to reopen service handle") - return fmt.Errorf("reopen service: %w", err) - } - } + if err != nil { + log.Error().Err(err).Msg("Failed to reopen service handle") + return fmt.Errorf("reopen service: %w", err) + } + } - return + return } diff --git a/pkg/goexec/scmr/module.go b/pkg/goexec/scmr/module.go index 56ce3fd..36010e8 100644 --- a/pkg/goexec/scmr/module.go +++ b/pkg/goexec/scmr/module.go @@ -1,195 +1,195 @@ package scmrexec import ( - "context" - "errors" - "fmt" - "github.com/FalconOpsLLC/goexec/internal/util" - "github.com/FalconOpsLLC/goexec/pkg/goexec" - "github.com/FalconOpsLLC/goexec/pkg/goexec/dce" - "github.com/oiweiwei/go-msrpc/dcerpc" - "github.com/oiweiwei/go-msrpc/midl/uuid" - "github.com/oiweiwei/go-msrpc/msrpc/scmr/svcctl/v2" - "github.com/rs/zerolog" + "context" + "errors" + "fmt" + "github.com/FalconOpsLLC/goexec/internal/util" + "github.com/FalconOpsLLC/goexec/pkg/goexec" + "github.com/FalconOpsLLC/goexec/pkg/goexec/dce" + "github.com/oiweiwei/go-msrpc/dcerpc" + "github.com/oiweiwei/go-msrpc/midl/uuid" + "github.com/oiweiwei/go-msrpc/msrpc/scmr/svcctl/v2" + "github.com/rs/zerolog" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32" ) type Scmr struct { - goexec.Cleaner + goexec.Cleaner - Client *dce.Client - ctl svcctl.SvcctlClient - scm *svcctl.Handle + Client *dce.Client + ctl svcctl.SvcctlClient + scm *svcctl.Handle - hostname string + hostname string } const ( - ModuleName = "SCMR" + ModuleName = "SCMR" - DefaultEndpoint = "ncacn_np:[svcctl]" - ScmrUuid = "367ABB81-9844-35F1-AD32-98F038001003" + DefaultEndpoint = "ncacn_np:[svcctl]" + ScmrUuid = "367ABB81-9844-35F1-AD32-98F038001003" ) func (m *Scmr) Connect(ctx context.Context) (err error) { - if err = m.Client.Connect(ctx); err == nil { - m.AddCleaners(m.Client.Close) - } - return + if err = m.Client.Connect(ctx); err == nil { + m.AddCleaners(m.Client.Close) + } + return } func (m *Scmr) Init(ctx context.Context) (err error) { - log := zerolog.Ctx(ctx).With(). - Str("module", ModuleName).Logger() + log := zerolog.Ctx(ctx).With(). + Str("module", ModuleName).Logger() - if m.Client == nil || m.Client.Dce() == nil { - return errors.New("DCE connection not initialized") - } + if m.Client == nil || m.Client.Dce() == nil { + return errors.New("DCE connection not initialized") + } - m.hostname, err = m.Client.Target.Hostname(ctx) - if err != nil { - log.Debug().Err(err).Msg("Failed to determine target hostname") - } - if m.hostname == "" { - m.hostname = util.RandomHostname() - } + m.hostname, err = m.Client.Target.Hostname(ctx) + if err != nil { + log.Debug().Err(err).Msg("Failed to determine target hostname") + } + if m.hostname == "" { + m.hostname = util.RandomHostname() + } - svcctlOpts := []dcerpc.Option{dcerpc.WithObjectUUID(uuid.MustParse(ScmrUuid))} + svcctlOpts := []dcerpc.Option{dcerpc.WithObjectUUID(uuid.MustParse(ScmrUuid))} - if m.Client.Smb { - svcctlOpts = append(svcctlOpts, dcerpc.WithInsecure()) - } + if m.Client.Smb { + svcctlOpts = append(svcctlOpts, dcerpc.WithInsecure()) + } - m.ctl, err = svcctl.NewSvcctlClient(ctx, m.Client.Dce(), svcctlOpts...) - if err != nil { - log.Error().Err(err).Msg("Failed to initialize SVCCTL client") - return fmt.Errorf("create SVCCTL client: %w", err) - } - log.Info().Msg("Created SVCCTL client") + m.ctl, err = svcctl.NewSvcctlClient(ctx, m.Client.Dce(), svcctlOpts...) + if err != nil { + log.Error().Err(err).Msg("Failed to initialize SVCCTL client") + return fmt.Errorf("create SVCCTL client: %w", err) + } + log.Info().Msg("Created SVCCTL client") - resp, err := m.ctl.OpenSCMW(ctx, &svcctl.OpenSCMWRequest{ - MachineName: m.hostname, - DatabaseName: "ServicesActive", - DesiredAccess: ServiceAllAccess, - }) - if err != nil { - log.Debug().Err(err).Msg("Failed to open SCM handle") - return fmt.Errorf("open SCM handle: %w", err) - } - log.Info().Msg("Opened SCM handle") + resp, err := m.ctl.OpenSCMW(ctx, &svcctl.OpenSCMWRequest{ + MachineName: m.hostname, + DatabaseName: "ServicesActive", + DesiredAccess: ServiceAllAccess, + }) + if err != nil { + log.Debug().Err(err).Msg("Failed to open SCM handle") + return fmt.Errorf("open SCM handle: %w", err) + } + log.Info().Msg("Opened SCM handle") - m.scm = resp.SCM + m.scm = resp.SCM - return + return } func (m *Scmr) Reconnect(ctx context.Context) (err error) { - if err = m.Client.Reconnect(ctx); err != nil { - return fmt.Errorf("reconnect: %w", err) - } - if err = m.Init(ctx); err != nil { - return fmt.Errorf("reconnect SCMR: %w", err) - } - return + if err = m.Client.Reconnect(ctx); err != nil { + return fmt.Errorf("reconnect: %w", err) + } + if err = m.Init(ctx); err != nil { + return fmt.Errorf("reconnect SCMR: %w", err) + } + return } // openService will a handle to the desired service func (m *Scmr) openService(ctx context.Context, name string) (svc *service, err error) { - log := zerolog.Ctx(ctx) + log := zerolog.Ctx(ctx) - resp, err := m.ctl.OpenServiceW(ctx, &svcctl.OpenServiceWRequest{ - ServiceManager: m.scm, - ServiceName: name, - DesiredAccess: ServiceAllAccess, // TODO: dynamic - }) - if err != nil { - log.Error().Err(err).Msg("Failed to open service handle") - return nil, fmt.Errorf("open service: %w", err) - } + resp, err := m.ctl.OpenServiceW(ctx, &svcctl.OpenServiceWRequest{ + ServiceManager: m.scm, + ServiceName: name, + DesiredAccess: ServiceAllAccess, // TODO: dynamic + }) + if err != nil { + log.Error().Err(err).Msg("Failed to open service handle") + return nil, fmt.Errorf("open service: %w", err) + } - log.Info().Msg("Opened service handle") + log.Info().Msg("Opened service handle") - svc = new(service) - svc.name = name - svc.handle = resp.Service + svc = new(service) + svc.name = name + svc.handle = resp.Service - return + return } func (m *Scmr) startService(ctx context.Context, svc *service) error { - log := zerolog.Ctx(ctx).With(). - Str("service", svc.name).Logger() + log := zerolog.Ctx(ctx).With(). + Str("service", svc.name).Logger() - sr, err := m.ctl.StartServiceW(ctx, &svcctl.StartServiceWRequest{Service: svc.handle}) + sr, err := m.ctl.StartServiceW(ctx, &svcctl.StartServiceWRequest{Service: svc.handle}) - if err != nil { + if err != nil { - if errors.Is(err, context.DeadlineExceeded) { // Check if execution timed out (execute "cmd.exe /c notepad" for test case) - log.Warn().Msg("Service execution deadline exceeded") - svc.handle = nil - return nil + if errors.Is(err, context.DeadlineExceeded) { // Check if execution timed out (execute "cmd.exe /c notepad" for test case) + log.Warn().Msg("Service execution deadline exceeded") + svc.handle = nil + return nil - } else if sr.Return == ErrorServiceRequestTimeout { - log.Info().Msg("Received request timeout. Execution was likely successful") - return nil - } + } else if sr.Return == ErrorServiceRequestTimeout { + log.Info().Msg("Received request timeout. Execution was likely successful") + return nil + } - log.Error().Err(err).Msg("Failed to start service") - return fmt.Errorf("start service: %w", err) - } - log.Info().Msg("Service started successfully") - return nil + log.Error().Err(err).Msg("Failed to start service") + return fmt.Errorf("start service: %w", err) + } + log.Info().Msg("Service started successfully") + return nil } func (m *Scmr) deleteService(ctx context.Context, svc *service) (err error) { - log := zerolog.Ctx(ctx).With(). - Str("service", svc.name).Logger() + log := zerolog.Ctx(ctx).With(). + Str("service", svc.name).Logger() - deleteResponse, err := m.ctl.DeleteService(ctx, &svcctl.DeleteServiceRequest{ - Service: svc.handle, - }) + deleteResponse, err := m.ctl.DeleteService(ctx, &svcctl.DeleteServiceRequest{ + Service: svc.handle, + }) - if err != nil { - log.Error().Err(err).Msg("Failed to delete service") - return fmt.Errorf("delete service: %w", err) - } + if err != nil { + log.Error().Err(err).Msg("Failed to delete service") + return fmt.Errorf("delete service: %w", err) + } - if deleteResponse.Return != 0 { - log.Error().Err(err).Str("code", fmt.Sprintf("0x%02x", deleteResponse.Return)).Msg("Failed to delete service") - return fmt.Errorf("delete service returned non-zero exit code: 0x%02x", deleteResponse.Return) - } + if deleteResponse.Return != 0 { + log.Error().Err(err).Str("code", fmt.Sprintf("0x%02x", deleteResponse.Return)).Msg("Failed to delete service") + return fmt.Errorf("delete service returned non-zero exit code: 0x%02x", deleteResponse.Return) + } - log.Info().Msg("Deleted service") - return + log.Info().Msg("Deleted service") + return } func (m *Scmr) closeService(ctx context.Context, svc *service) (err error) { - log := zerolog.Ctx(ctx).With(). - Str("service", svc.name).Logger() + log := zerolog.Ctx(ctx).With(). + Str("service", svc.name).Logger() - closResponse, err := m.ctl.CloseService(ctx, &svcctl.CloseServiceRequest{ - ServiceObject: svc.handle, - }) + closResponse, err := m.ctl.CloseService(ctx, &svcctl.CloseServiceRequest{ + ServiceObject: svc.handle, + }) - if err != nil { - log.Error().Err(err).Msg("Failed to close service handle") - return fmt.Errorf("close service: %w", err) - } + if err != nil { + log.Error().Err(err).Msg("Failed to close service handle") + return fmt.Errorf("close service: %w", err) + } - if closResponse.Return != 0 { - log.Error().Err(err).Str("code", fmt.Sprintf("0x%02x", closResponse.Return)).Msg("Failed to close service handle") - return fmt.Errorf("close service returned non-zero exit code: 0x%02x", closResponse.Return) - } + if closResponse.Return != 0 { + log.Error().Err(err).Str("code", fmt.Sprintf("0x%02x", closResponse.Return)).Msg("Failed to close service handle") + return fmt.Errorf("close service returned non-zero exit code: 0x%02x", closResponse.Return) + } - log.Info().Msg("Closed service handle") - return + log.Info().Msg("Closed service handle") + return } diff --git a/pkg/goexec/scmr/scmr.go b/pkg/goexec/scmr/scmr.go index 3bf416c..b3a5c09 100644 --- a/pkg/goexec/scmr/scmr.go +++ b/pkg/goexec/scmr/scmr.go @@ -1,59 +1,59 @@ package scmrexec import ( - "github.com/oiweiwei/go-msrpc/msrpc/scmr/svcctl/v2" - "golang.org/x/text/encoding/unicode" - "strings" + "github.com/oiweiwei/go-msrpc/msrpc/scmr/svcctl/v2" + "golang.org/x/text/encoding/unicode" + "strings" ) const ( - ErrorServiceRequestTimeout uint32 = 0x0000041d - ErrorServiceNotActive uint32 = 0x00000426 + ErrorServiceRequestTimeout uint32 = 0x0000041d + ErrorServiceNotActive uint32 = 0x00000426 - ServiceDemandStart uint32 = 0x00000003 - ServiceWin32OwnProcess uint32 = 0x00000010 + ServiceDemandStart uint32 = 0x00000003 + ServiceWin32OwnProcess uint32 = 0x00000010 - // https://learn.microsoft.com/en-us/windows/win32/services/service-security-and-access-rights + // https://learn.microsoft.com/en-us/windows/win32/services/service-security-and-access-rights - ServiceQueryConfig uint32 = 0x00000001 - ServiceChangeConfig uint32 = 0x00000002 - ServiceStart uint32 = 0x00000010 - ServiceStop uint32 = 0x00000020 - ServiceDelete uint32 = 0x00010000 // special permission - ServiceControlStop uint32 = 0x00000001 - ScManagerCreateService uint32 = 0x00000002 + ServiceQueryConfig uint32 = 0x00000001 + ServiceChangeConfig uint32 = 0x00000002 + ServiceStart uint32 = 0x00000010 + ServiceStop uint32 = 0x00000020 + ServiceDelete uint32 = 0x00010000 // special permission + ServiceControlStop uint32 = 0x00000001 + ScManagerCreateService uint32 = 0x00000002 - /* - // Windows error codes - ERROR_FILE_NOT_FOUND uint32 = 0x00000002 - ERROR_SERVICE_DOES_NOT_EXIST uint32 = 0x00000424 + /* + // Windows error codes + ERROR_FILE_NOT_FOUND uint32 = 0x00000002 + ERROR_SERVICE_DOES_NOT_EXIST uint32 = 0x00000424 - // Windows service/scm constants - SERVICE_BOOT_START uint32 = 0x00000000 - SERVICE_SYSTEM_START uint32 = 0x00000001 - SERVICE_AUTO_START uint32 = 0x00000002 - SERVICE_DISABLED uint32 = 0x00000004 + // Windows service/scm constants + SERVICE_BOOT_START uint32 = 0x00000000 + SERVICE_SYSTEM_START uint32 = 0x00000001 + SERVICE_AUTO_START uint32 = 0x00000002 + SERVICE_DISABLED uint32 = 0x00000004 - // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-scmr/4e91ff36-ab5f-49ed-a43d-a308e72b0b3c - SERVICE_CONTINUE_PENDING uint32 = 0x00000005 - SERVICE_PAUSE_PENDING uint32 = 0x00000006 - SERVICE_PAUSED uint32 = 0x00000007 - SERVICE_RUNNING uint32 = 0x00000004 - SERVICE_START_PENDING uint32 = 0x00000002 - SERVICE_STOP_PENDING uint32 = 0x00000003 - SERVICE_STOPPED uint32 = 0x00000001 - */ + // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-scmr/4e91ff36-ab5f-49ed-a43d-a308e72b0b3c + SERVICE_CONTINUE_PENDING uint32 = 0x00000005 + SERVICE_PAUSE_PENDING uint32 = 0x00000006 + SERVICE_PAUSED uint32 = 0x00000007 + SERVICE_RUNNING uint32 = 0x00000004 + SERVICE_START_PENDING uint32 = 0x00000002 + SERVICE_STOP_PENDING uint32 = 0x00000003 + SERVICE_STOPPED uint32 = 0x00000001 + */ - ServiceDeleteAccess = ServiceDelete - ServiceModifyAccess = ServiceQueryConfig | ServiceChangeConfig | ServiceStop | ServiceStart | ServiceDelete - ServiceCreateAccess = ScManagerCreateService | ServiceStart | ServiceStop | ServiceDelete - ServiceAllAccess = ServiceCreateAccess | ServiceModifyAccess + ServiceDeleteAccess = ServiceDelete + ServiceModifyAccess = ServiceQueryConfig | ServiceChangeConfig | ServiceStop | ServiceStart | ServiceDelete + ServiceCreateAccess = ScManagerCreateService | ServiceStart | ServiceStop | ServiceDelete + ServiceAllAccess = ServiceCreateAccess | ServiceModifyAccess ) type service struct { - name string - handle *svcctl.Handle - originalConfig *svcctl.QueryServiceConfigW + name string + handle *svcctl.Handle + originalConfig *svcctl.QueryServiceConfigW } // parseDependencies will parse the dependencies returned from a RQueryServiceConfigW @@ -61,13 +61,13 @@ type service struct { // the lpDependencies field as defined in the microsoft docs. // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-scmr/3ab258d6-87b0-459e-8d83-a2cdd8038b78 func parseDependencies(deps string) (out []byte) { - if deps != "" && deps != "/" { + if deps != "" && deps != "/" { - if out, err := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder().Bytes( - []byte(strings.ReplaceAll(deps, "/", "\x00") + "\x00"), - ); err == nil { - return out - } - } - return nil + if out, err := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder().Bytes( + []byte(strings.ReplaceAll(deps, "/", "\x00") + "\x00"), + ); err == nil { + return out + } + } + return nil } diff --git a/pkg/goexec/tsch/change.go b/pkg/goexec/tsch/change.go index 908b25a..42c5be1 100644 --- a/pkg/goexec/tsch/change.go +++ b/pkg/goexec/tsch/change.go @@ -1,155 +1,155 @@ package tschexec import ( - "context" - "encoding/xml" - "fmt" - "github.com/FalconOpsLLC/goexec/pkg/goexec" - "github.com/FalconOpsLLC/goexec/pkg/goexec/tsch/task" - "github.com/oiweiwei/go-msrpc/msrpc/tsch/itaskschedulerservice/v1" - "github.com/rs/zerolog" - "regexp" - "time" + "context" + "encoding/xml" + "fmt" + "github.com/FalconOpsLLC/goexec/pkg/goexec" + "github.com/FalconOpsLLC/goexec/pkg/goexec/tsch/task" + "github.com/oiweiwei/go-msrpc/msrpc/tsch/itaskschedulerservice/v1" + "github.com/rs/zerolog" + "regexp" + "time" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32" ) const ( - FlagTaskUpdate uint32 = 0b_00000000_00000000_00000000_00000100 - MethodChange = "Change" - DefaultWaitTime = 1 * time.Second + FlagTaskUpdate uint32 = 0b_00000000_00000000_00000000_00000100 + MethodChange = "Change" + DefaultWaitTime = 1 * time.Second ) type TschChange struct { - Tsch - goexec.Executor - goexec.Cleaner + Tsch + goexec.Executor + goexec.Cleaner - IO goexec.ExecutionIO + IO goexec.ExecutionIO - WorkingDirectory string - NoStart bool - NoRevert bool - WaitTime time.Duration + WorkingDirectory string + NoStart bool + NoRevert bool + WaitTime time.Duration } func (m *TschChange) Execute(ctx context.Context, execIO *goexec.ExecutionIO) (err error) { - log := zerolog.Ctx(ctx).With(). - Str("module", ModuleName). - Str("method", MethodChange). - Str("task", m.TaskPath). - Logger() + log := zerolog.Ctx(ctx).With(). + Str("module", ModuleName). + Str("method", MethodChange). + Str("task", m.TaskPath). + Logger() - retrieveResponse, err := m.tsch.RetrieveTask(ctx, &itaskschedulerservice.RetrieveTaskRequest{ - Path: m.TaskPath, - }) + retrieveResponse, err := m.tsch.RetrieveTask(ctx, &itaskschedulerservice.RetrieveTaskRequest{ + Path: m.TaskPath, + }) - if err != nil { - log.Error().Err(err).Msg("Failed to retrieve task") - return fmt.Errorf("retrieve task: %w", err) - } - if retrieveResponse.Return != 0 { - log.Error().Err(err).Str("code", fmt.Sprintf("0x%02x", retrieveResponse.Return)). - Msg("Failed to retrieve task") - return fmt.Errorf("retrieve task returned non-zero exit code: %02x", retrieveResponse.Return) - } + if err != nil { + log.Error().Err(err).Msg("Failed to retrieve task") + return fmt.Errorf("retrieve task: %w", err) + } + if retrieveResponse.Return != 0 { + log.Error().Err(err).Str("code", fmt.Sprintf("0x%02x", retrieveResponse.Return)). + Msg("Failed to retrieve task") + return fmt.Errorf("retrieve task returned non-zero exit code: %02x", retrieveResponse.Return) + } - log.Info().Msg("Successfully retrieved existing task definition") - log.Debug().Str("xml", retrieveResponse.XML).Msg("Got task definition") + log.Info().Msg("Successfully retrieved existing task definition") + log.Debug().Str("xml", retrieveResponse.XML).Msg("Got task definition") - tk := task.Task{} + tk := task.Task{} - enc := regexp.MustCompile(`(?i)^<\?xml .*?\?>`) - tkStr := enc.ReplaceAllString(retrieveResponse.XML, ``) + enc := regexp.MustCompile(`(?i)^<\?xml .*?\?>`) + tkStr := enc.ReplaceAllString(retrieveResponse.XML, ``) - if err = xml.Unmarshal([]byte(tkStr), &tk); err != nil { - log.Error().Err(err).Msg("Failed to unmarshal task XML") + if err = xml.Unmarshal([]byte(tkStr), &tk); err != nil { + log.Error().Err(err).Msg("Failed to unmarshal task XML") - return fmt.Errorf("unmarshal task XML: %w", err) - } + return fmt.Errorf("unmarshal task XML: %w", err) + } - cmd := execIO.CommandLine() + cmd := execIO.CommandLine() - tk.Actions.Exec = append(tk.Actions.Exec, task.ExecAction{ - Command: cmd[0], - Arguments: cmd[1], - WorkingDirectory: m.WorkingDirectory, - }) + tk.Actions.Exec = append(tk.Actions.Exec, task.ExecAction{ + Command: cmd[0], + Arguments: cmd[1], + WorkingDirectory: m.WorkingDirectory, + }) - doc, err := xml.Marshal(tk) + doc, err := xml.Marshal(tk) - if err != nil { - log.Error().Err(err).Msg("failed to marshal task XML") - return fmt.Errorf("marshal task: %w", err) - } + if err != nil { + log.Error().Err(err).Msg("failed to marshal task XML") + return fmt.Errorf("marshal task: %w", err) + } - taskXml := TaskXmlHeader + string(doc) - log.Debug().Str("xml", taskXml).Msg("Serialized new task") + taskXml := TaskXmlHeader + string(doc) + log.Debug().Str("xml", taskXml).Msg("Serialized new task") - registerResponse, err := m.tsch.RegisterTask(ctx, &itaskschedulerservice.RegisterTaskRequest{ - Path: m.TaskPath, - XML: taskXml, - Flags: FlagTaskUpdate, - }) + registerResponse, err := m.tsch.RegisterTask(ctx, &itaskschedulerservice.RegisterTaskRequest{ + Path: m.TaskPath, + XML: taskXml, + Flags: FlagTaskUpdate, + }) - if !m.NoRevert { + if !m.NoRevert { - m.AddCleaners(func(ctxInner context.Context) error { + m.AddCleaners(func(ctxInner context.Context) error { - revertResponse, err := m.tsch.RegisterTask(ctx, &itaskschedulerservice.RegisterTaskRequest{ - Path: m.TaskPath, - XML: retrieveResponse.XML, - Flags: FlagTaskUpdate, - }) + revertResponse, err := m.tsch.RegisterTask(ctx, &itaskschedulerservice.RegisterTaskRequest{ + Path: m.TaskPath, + XML: retrieveResponse.XML, + Flags: FlagTaskUpdate, + }) - if err != nil { - return err - } - if revertResponse.Return != 0 { - return fmt.Errorf("revert task definition returned non-zero exit code: %02x", revertResponse.Return) - } - return nil - }) - } + if err != nil { + return err + } + if revertResponse.Return != 0 { + return fmt.Errorf("revert task definition returned non-zero exit code: %02x", revertResponse.Return) + } + return nil + }) + } - if err != nil { - log.Error().Err(err).Msg("Failed to update task") + if err != nil { + log.Error().Err(err).Msg("Failed to update task") - return fmt.Errorf("update task: %w", err) - } - if registerResponse.Return != 0 { - log.Error().Err(err).Str("code", fmt.Sprintf("0x%02x", registerResponse.Return)).Msg("Failed to update task definition") + return fmt.Errorf("update task: %w", err) + } + if registerResponse.Return != 0 { + log.Error().Err(err).Str("code", fmt.Sprintf("0x%02x", registerResponse.Return)).Msg("Failed to update task definition") - return fmt.Errorf("update task returned non-zero exit code: %02x", registerResponse.Return) - } - log.Info().Msg("Successfully updated task definition") + return fmt.Errorf("update task returned non-zero exit code: %02x", registerResponse.Return) + } + log.Info().Msg("Successfully updated task definition") - if !m.NoStart { + if !m.NoStart { - runResponse, err := m.tsch.Run(ctx, &itaskschedulerservice.RunRequest{ - Path: m.TaskPath, - }) + runResponse, err := m.tsch.Run(ctx, &itaskschedulerservice.RunRequest{ + Path: m.TaskPath, + }) - if err != nil { - log.Error().Err(err).Msg("Failed to run modified task") + if err != nil { + log.Error().Err(err).Msg("Failed to run modified task") - return fmt.Errorf("run task: %w", err) - } + return fmt.Errorf("run task: %w", err) + } - if ret := uint32(runResponse.Return); ret != 0 { - log.Error().Str("code", fmt.Sprintf("0x%08x", ret)).Msg("Run task returned non-zero exit code") + if ret := uint32(runResponse.Return); ret != 0 { + log.Error().Str("code", fmt.Sprintf("0x%08x", ret)).Msg("Run task returned non-zero exit code") - return fmt.Errorf("run task returned non-zero exit code: 0x%08x", ret) - } + return fmt.Errorf("run task returned non-zero exit code: 0x%08x", ret) + } - log.Info().Msg("Successfully started modified task") - } + log.Info().Msg("Successfully started modified task") + } - if m.WaitTime <= 0 { - m.WaitTime = DefaultWaitTime - } - time.Sleep(m.WaitTime) - return + if m.WaitTime <= 0 { + m.WaitTime = DefaultWaitTime + } + time.Sleep(m.WaitTime) + return } diff --git a/pkg/goexec/tsch/demand.go b/pkg/goexec/tsch/demand.go index 62e546f..05da947 100644 --- a/pkg/goexec/tsch/demand.go +++ b/pkg/goexec/tsch/demand.go @@ -1,84 +1,84 @@ package tschexec import ( - "context" - "fmt" - "github.com/FalconOpsLLC/goexec/pkg/goexec" - "github.com/oiweiwei/go-msrpc/msrpc/tsch/itaskschedulerservice/v1" - "github.com/rs/zerolog" + "context" + "fmt" + "github.com/FalconOpsLLC/goexec/pkg/goexec" + "github.com/oiweiwei/go-msrpc/msrpc/tsch/itaskschedulerservice/v1" + "github.com/rs/zerolog" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32" ) const ( - MethodDemand = "Demand" + MethodDemand = "Demand" ) type TschDemand struct { - Tsch - goexec.Executor - goexec.Cleaner + Tsch + goexec.Executor + goexec.Cleaner - IO goexec.ExecutionIO + IO goexec.ExecutionIO - NoDelete bool - NoStart bool - SessionId uint32 + NoDelete bool + NoStart bool + SessionId uint32 } func (m *TschDemand) Execute(ctx context.Context, execIO *goexec.ExecutionIO) (err error) { - log := zerolog.Ctx(ctx).With(). - Str("module", ModuleName). - Str("method", MethodDemand). - Str("task", m.TaskPath). - Logger() + log := zerolog.Ctx(ctx).With(). + Str("module", ModuleName). + Str("method", MethodDemand). + Str("task", m.TaskPath). + Logger() - path, err := m.registerTask(ctx, - ®isterOptions{ - AllowStartOnDemand: true, - AllowHardTerminate: true, - Hidden: !m.NotHidden, - triggers: taskTriggers{}, - }, - execIO, - ) - if err != nil { - return err - } + path, err := m.registerTask(ctx, + ®isterOptions{ + AllowStartOnDemand: true, + AllowHardTerminate: true, + Hidden: !m.NotHidden, + triggers: taskTriggers{}, + }, + execIO, + ) + if err != nil { + return err + } - log.Info().Msg("Task registered") + log.Info().Msg("Task registered") - if !m.NoDelete { - m.AddCleaners(func(ctxInner context.Context) error { - return m.deleteTask(ctxInner, path) - }) - } + if !m.NoDelete { + m.AddCleaners(func(ctxInner context.Context) error { + return m.deleteTask(ctxInner, path) + }) + } - if !m.NoStart { + if !m.NoStart { - var flags uint32 - if m.SessionId != 0 { - flags |= 4 - } + var flags uint32 + if m.SessionId != 0 { + flags |= 4 + } - runResponse, err := m.tsch.Run(ctx, &itaskschedulerservice.RunRequest{ - Path: path, - Flags: flags, - SessionID: m.SessionId, - }) + runResponse, err := m.tsch.Run(ctx, &itaskschedulerservice.RunRequest{ + Path: path, + Flags: flags, + SessionID: m.SessionId, + }) - if err != nil { - log.Error().Err(err).Msg("Failed to run task") - return fmt.Errorf("run task: %w", err) - } - if ret := uint32(runResponse.Return); ret != 0 { - log.Error().Str("code", fmt.Sprintf("0x%08x", ret)).Msg("Task returned non-zero exit code") - return fmt.Errorf("task returned non-zero exit code: 0x%08x", ret) - } + if err != nil { + log.Error().Err(err).Msg("Failed to run task") + return fmt.Errorf("run task: %w", err) + } + if ret := uint32(runResponse.Return); ret != 0 { + log.Error().Str("code", fmt.Sprintf("0x%08x", ret)).Msg("Task returned non-zero exit code") + return fmt.Errorf("task returned non-zero exit code: 0x%08x", ret) + } - log.Info().Msg("Task started successfully") - } - return + log.Info().Msg("Task started successfully") + } + return } diff --git a/pkg/goexec/tsch/module.go b/pkg/goexec/tsch/module.go index 020c35e..589ecf3 100644 --- a/pkg/goexec/tsch/module.go +++ b/pkg/goexec/tsch/module.go @@ -1,176 +1,176 @@ package tschexec import ( - "context" - "encoding/xml" - "errors" - "fmt" - "github.com/FalconOpsLLC/goexec/pkg/goexec" - "github.com/FalconOpsLLC/goexec/pkg/goexec/dce" - "github.com/oiweiwei/go-msrpc/dcerpc" - "github.com/oiweiwei/go-msrpc/msrpc/tsch/itaskschedulerservice/v1" - "github.com/rs/zerolog" - "strings" + "context" + "encoding/xml" + "errors" + "fmt" + "github.com/FalconOpsLLC/goexec/pkg/goexec" + "github.com/FalconOpsLLC/goexec/pkg/goexec/dce" + "github.com/oiweiwei/go-msrpc/dcerpc" + "github.com/oiweiwei/go-msrpc/msrpc/tsch/itaskschedulerservice/v1" + "github.com/rs/zerolog" + "strings" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32" ) const ( - ModuleName = "TSCH" + ModuleName = "TSCH" ) type Tsch struct { - goexec.Cleaner + goexec.Cleaner - Client *dce.Client - tsch itaskschedulerservice.TaskSchedulerServiceClient + Client *dce.Client + tsch itaskschedulerservice.TaskSchedulerServiceClient - TaskPath string - UserSid string - NotHidden bool + TaskPath string + UserSid string + NotHidden bool } type registerOptions struct { - AllowStartOnDemand bool - AllowHardTerminate bool - StartWhenAvailable bool - Hidden bool - DeleteAfter string + AllowStartOnDemand bool + AllowHardTerminate bool + StartWhenAvailable bool + Hidden bool + DeleteAfter string - triggers taskTriggers + triggers taskTriggers } func (m *Tsch) Connect(ctx context.Context) (err error) { - if err = m.Client.Connect(ctx); err == nil { - m.AddCleaners(m.Client.Close) - } - return + if err = m.Client.Connect(ctx); err == nil { + m.AddCleaners(m.Client.Close) + } + return } func (m *Tsch) Init(ctx context.Context) (err error) { - if m.Client.Dce() == nil { - return errors.New("DCE connection not initialized") - } + if m.Client.Dce() == nil { + return errors.New("DCE connection not initialized") + } - // Create ITaskSchedulerService Client - m.tsch, err = itaskschedulerservice.NewTaskSchedulerServiceClient(ctx, m.Client.Dce(), dcerpc.WithSeal()) - return + // Create ITaskSchedulerService Client + m.tsch, err = itaskschedulerservice.NewTaskSchedulerServiceClient(ctx, m.Client.Dce(), dcerpc.WithSeal()) + return } func (m *Tsch) registerTask(ctx context.Context, opts *registerOptions, in *goexec.ExecutionIO) (path string, err error) { - log := zerolog.Ctx(ctx).With(). - Str("task", m.TaskPath). - Logger() + log := zerolog.Ctx(ctx).With(). + Str("task", m.TaskPath). + Logger() - ctx = log.WithContext(ctx) + ctx = log.WithContext(ctx) - principalId := "LocalSystem" + principalId := "LocalSystem" - settings := taskSettings{ - MultipleInstancesPolicy: "IgnoreNew", - IdleSettings: taskIdleSettings{ - StopOnIdleEnd: true, - RestartOnIdle: false, - }, - Enabled: true, - Priority: 7, // a pretty standard value for scheduled tasks - AllowHardTerminate: opts.AllowHardTerminate, - AllowStartOnDemand: opts.AllowStartOnDemand, - Hidden: opts.Hidden, - StartWhenAvailable: opts.StartWhenAvailable, - DeleteExpiredTaskAfter: opts.DeleteAfter, - } + settings := taskSettings{ + MultipleInstancesPolicy: "IgnoreNew", + IdleSettings: taskIdleSettings{ + StopOnIdleEnd: true, + RestartOnIdle: false, + }, + Enabled: true, + Priority: 7, // a pretty standard value for scheduled tasks + AllowHardTerminate: opts.AllowHardTerminate, + AllowStartOnDemand: opts.AllowStartOnDemand, + Hidden: opts.Hidden, + StartWhenAvailable: opts.StartWhenAvailable, + DeleteExpiredTaskAfter: opts.DeleteAfter, + } - principals := taskPrincipals{ - Principals: []taskPrincipal{ - { - ID: principalId, - UserID: m.UserSid, - RunLevel: "HighestAvailable", - }, - }} + principals := taskPrincipals{ + Principals: []taskPrincipal{ + { + ID: principalId, + UserID: m.UserSid, + RunLevel: "HighestAvailable", + }, + }} - var cmd, args string + var cmd, args string - cmdline := in.CommandLine() + cmdline := in.CommandLine() - if l := len(cmdline); l >= 1 { - cmd = cmdline[0] - if l >= 2 { - args = strings.Join(cmdline[1:], " ") - } - } + if l := len(cmdline); l >= 1 { + cmd = cmdline[0] + if l >= 2 { + args = strings.Join(cmdline[1:], " ") + } + } - actions := taskActions{ - Context: principalId, - Exec: []taskActionExec{ - { - Command: cmd, - Arguments: args, - }, - }, - } + actions := taskActions{ + Context: principalId, + Exec: []taskActionExec{ + { + Command: cmd, + Arguments: args, + }, + }, + } - def := simpleTask{ - TaskVersion: TaskXmlVersion, - TaskNamespace: TaskXmlNamespace, - Triggers: opts.triggers, - Actions: actions, - Principals: principals, - Settings: settings, - } + def := simpleTask{ + TaskVersion: TaskXmlVersion, + TaskNamespace: TaskXmlNamespace, + Triggers: opts.triggers, + Actions: actions, + Principals: principals, + Settings: settings, + } - // Generate task XML content. See https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsch/0d6383e4-de92-43e7-b0bb-a60cfa36379f + // Generate task XML content. See https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsch/0d6383e4-de92-43e7-b0bb-a60cfa36379f - doc, err := xml.Marshal(def) + doc, err := xml.Marshal(def) - if err != nil { - log.Error().Err(err).Msg("failed to marshal task XML") - return "", fmt.Errorf("marshal task: %w", err) - } + if err != nil { + log.Error().Err(err).Msg("failed to marshal task XML") + return "", fmt.Errorf("marshal task: %w", err) + } - taskXml := TaskXmlHeader + string(doc) + taskXml := TaskXmlHeader + string(doc) - log.Debug().Str("content", taskXml).Msg("Generated task XML") + log.Debug().Str("content", taskXml).Msg("Generated task XML") - registerResponse, err := m.tsch.RegisterTask(ctx, &itaskschedulerservice.RegisterTaskRequest{ - Path: m.TaskPath, - XML: taskXml, - Flags: 0, // FEATURE: dynamic - SDDL: "", - LogonType: 0, // FEATURE: dynamic - CredsCount: 0, - Creds: nil, - }) + registerResponse, err := m.tsch.RegisterTask(ctx, &itaskschedulerservice.RegisterTaskRequest{ + Path: m.TaskPath, + XML: taskXml, + Flags: 0, // FEATURE: dynamic + SDDL: "", + LogonType: 0, // FEATURE: dynamic + CredsCount: 0, + Creds: nil, + }) - if err != nil { - log.Error().Err(err).Msg("Failed to register task") - return "", fmt.Errorf("register task: %w", err) - } - log.Info().Msg("Scheduled task registered") + if err != nil { + log.Error().Err(err).Msg("Failed to register task") + return "", fmt.Errorf("register task: %w", err) + } + log.Info().Msg("Scheduled task registered") - return registerResponse.ActualPath, nil + return registerResponse.ActualPath, nil } func (m *Tsch) deleteTask(ctx context.Context, taskPath string) (err error) { - log := zerolog.Ctx(ctx).With(). - Str("path", taskPath).Logger() + log := zerolog.Ctx(ctx).With(). + Str("path", taskPath).Logger() - _, err = m.tsch.Delete(ctx, &itaskschedulerservice.DeleteRequest{ - Path: taskPath, - }) + _, err = m.tsch.Delete(ctx, &itaskschedulerservice.DeleteRequest{ + Path: taskPath, + }) - if err != nil { - log.Error().Err(err).Msg("Failed to delete task") - return fmt.Errorf("delete task: %w", err) - } + if err != nil { + log.Error().Err(err).Msg("Failed to delete task") + return fmt.Errorf("delete task: %w", err) + } - log.Info().Msg("Task deleted") + log.Info().Msg("Task deleted") - return + return } diff --git a/pkg/goexec/wmi/module.go b/pkg/goexec/wmi/module.go index b21c3e3..962d666 100644 --- a/pkg/goexec/wmi/module.go +++ b/pkg/goexec/wmi/module.go @@ -1,145 +1,145 @@ package wmiexec import ( - "context" - "errors" - "fmt" - "github.com/FalconOpsLLC/goexec/pkg/goexec" - "github.com/FalconOpsLLC/goexec/pkg/goexec/dce" - "github.com/oiweiwei/go-msrpc/dcerpc" - "github.com/oiweiwei/go-msrpc/msrpc/dcom" - "github.com/oiweiwei/go-msrpc/msrpc/dcom/iactivation/v0" - "github.com/oiweiwei/go-msrpc/msrpc/dcom/wmi" - "github.com/oiweiwei/go-msrpc/msrpc/dcom/wmi/iwbemlevel1login/v0" - "github.com/oiweiwei/go-msrpc/msrpc/dcom/wmi/iwbemservices/v0" - "github.com/oiweiwei/go-msrpc/msrpc/dcom/wmio/query" - "github.com/rs/zerolog" + "context" + "errors" + "fmt" + "github.com/FalconOpsLLC/goexec/pkg/goexec" + "github.com/FalconOpsLLC/goexec/pkg/goexec/dce" + "github.com/oiweiwei/go-msrpc/dcerpc" + "github.com/oiweiwei/go-msrpc/msrpc/dcom" + "github.com/oiweiwei/go-msrpc/msrpc/dcom/iactivation/v0" + "github.com/oiweiwei/go-msrpc/msrpc/dcom/wmi" + "github.com/oiweiwei/go-msrpc/msrpc/dcom/wmi/iwbemlevel1login/v0" + "github.com/oiweiwei/go-msrpc/msrpc/dcom/wmi/iwbemservices/v0" + "github.com/oiweiwei/go-msrpc/msrpc/dcom/wmio/query" + "github.com/rs/zerolog" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32" - _ "github.com/oiweiwei/go-msrpc/msrpc/erref/wmi" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32" + _ "github.com/oiweiwei/go-msrpc/msrpc/erref/wmi" ) const ( - ModuleName = "WMI" - DefaultEndpoint = "ncacn_ip_tcp:[135]" + ModuleName = "WMI" + DefaultEndpoint = "ncacn_ip_tcp:[135]" ) type Wmi struct { - goexec.Cleaner - Client *dce.Client + goexec.Cleaner + Client *dce.Client - Resource string + Resource string - servicesClient iwbemservices.ServicesClient + servicesClient iwbemservices.ServicesClient } func (m *Wmi) Connect(ctx context.Context) (err error) { - if err = m.Client.Connect(ctx); err == nil { - m.AddCleaners(m.Client.Close) - } - return + if err = m.Client.Connect(ctx); err == nil { + m.AddCleaners(m.Client.Close) + } + return } func (m *Wmi) Init(ctx context.Context) (err error) { - log := zerolog.Ctx(ctx).With(). - Str("module", ModuleName).Logger() + log := zerolog.Ctx(ctx).With(). + Str("module", ModuleName).Logger() - if m.Client == nil || m.Client.Dce() == nil { - return errors.New("DCE connection not initialized") - } + if m.Client == nil || m.Client.Dce() == nil { + return errors.New("DCE connection not initialized") + } - actClient, err := iactivation.NewActivationClient(ctx, m.Client.Dce()) - if err != nil { - log.Error().Err(err).Msg("Failed to initialize IActivation client") - return fmt.Errorf("create IActivation client: %w", err) - } + actClient, err := iactivation.NewActivationClient(ctx, m.Client.Dce()) + if err != nil { + log.Error().Err(err).Msg("Failed to initialize IActivation client") + return fmt.Errorf("create IActivation client: %w", err) + } - actResponse, err := actClient.RemoteActivation(ctx, &iactivation.RemoteActivationRequest{ - ORPCThis: ORPCThis, - ClassID: wmi.Level1LoginClassID.GUID(), - IIDs: []*dcom.IID{iwbemlevel1login.Level1LoginIID}, - RequestedProtocolSequences: []uint16{ProtocolSequenceRPC}, // FEATURE: Named pipe support? - }) - if err != nil { - log.Error().Err(err).Msg("Failed to activate remote object") - return fmt.Errorf("request remote activation: %w", err) - } - if actResponse.HResult != 0 { - return fmt.Errorf("remote activation failed with code %d", actResponse.HResult) - } + actResponse, err := actClient.RemoteActivation(ctx, &iactivation.RemoteActivationRequest{ + ORPCThis: ORPCThis, + ClassID: wmi.Level1LoginClassID.GUID(), + IIDs: []*dcom.IID{iwbemlevel1login.Level1LoginIID}, + RequestedProtocolSequences: []uint16{ProtocolSequenceRPC}, // FEATURE: Named pipe support? + }) + if err != nil { + log.Error().Err(err).Msg("Failed to activate remote object") + return fmt.Errorf("request remote activation: %w", err) + } + if actResponse.HResult != 0 { + return fmt.Errorf("remote activation failed with code %d", actResponse.HResult) + } - log.Info().Msg("Remote activation succeeded") + log.Info().Msg("Remote activation succeeded") - var newOpts []dcerpc.Option + var newOpts []dcerpc.Option - for _, bind := range actResponse.OXIDBindings.GetStringBindings() { - stringBinding, err := dcerpc.ParseStringBinding(bind.String()) - if err != nil { - log.Debug().Err(err).Msg("Failed to parse string binding") - continue - } - // Only consider ncacn_ip_tcp endpoints - if stringBinding.ProtocolSequence == dcerpc.ProtocolSequenceIPTCP { - stringBinding.NetworkAddress = m.Client.Target.AddressWithoutPort() - newOpts = append(newOpts, dcerpc.WithEndpoint(stringBinding.String())) - } - } + for _, bind := range actResponse.OXIDBindings.GetStringBindings() { + stringBinding, err := dcerpc.ParseStringBinding(bind.String()) + if err != nil { + log.Debug().Err(err).Msg("Failed to parse string binding") + continue + } + // Only consider ncacn_ip_tcp endpoints + if stringBinding.ProtocolSequence == dcerpc.ProtocolSequenceIPTCP { + stringBinding.NetworkAddress = m.Client.Target.AddressWithoutPort() + newOpts = append(newOpts, dcerpc.WithEndpoint(stringBinding.String())) + } + } - if err = m.Client.Reconnect(ctx, newOpts...); err != nil { - log.Error().Err(err).Msg("Failed to connect to remote instance") - return fmt.Errorf("connect remote instance: %w", err) - } + if err = m.Client.Reconnect(ctx, newOpts...); err != nil { + log.Error().Err(err).Msg("Failed to connect to remote instance") + return fmt.Errorf("connect remote instance: %w", err) + } - log.Info().Msg("Connected to remote instance") + log.Info().Msg("Connected to remote instance") - ipid := actResponse.InterfaceData[0].GetStandardObjectReference().Std.IPID - loginClient, err := iwbemlevel1login.NewLevel1LoginClient(ctx, m.Client.Dce(), dcom.WithIPID(ipid)) + ipid := actResponse.InterfaceData[0].GetStandardObjectReference().Std.IPID + loginClient, err := iwbemlevel1login.NewLevel1LoginClient(ctx, m.Client.Dce(), dcom.WithIPID(ipid)) - if err != nil { - log.Error().Err(err).Msg("Failed to create IWbemLevel1Login client") - return fmt.Errorf("create IWbemLevel1Login client: %w", err) - } + if err != nil { + log.Error().Err(err).Msg("Failed to create IWbemLevel1Login client") + return fmt.Errorf("create IWbemLevel1Login client: %w", err) + } - login, err := loginClient.NTLMLogin(ctx, &iwbemlevel1login.NTLMLoginRequest{ - This: ORPCThis, - NetworkResource: m.Resource, - }) + login, err := loginClient.NTLMLogin(ctx, &iwbemlevel1login.NTLMLoginRequest{ + This: ORPCThis, + NetworkResource: m.Resource, + }) - if err != nil { - log.Error().Err(err).Msg("Failed to login on remote instance") - return fmt.Errorf("login: IWbemLevel1Login::NTLMLogin: %w", err) - } + if err != nil { + log.Error().Err(err).Msg("Failed to login on remote instance") + return fmt.Errorf("login: IWbemLevel1Login::NTLMLogin: %w", err) + } - log.Info().Msg("Completed NTLMLogin operation") + log.Info().Msg("Completed NTLMLogin operation") - ipid = login.Namespace.InterfacePointer().IPID() - m.servicesClient, err = iwbemservices.NewServicesClient(ctx, m.Client.Dce(), dcom.WithIPID(ipid)) + ipid = login.Namespace.InterfacePointer().IPID() + m.servicesClient, err = iwbemservices.NewServicesClient(ctx, m.Client.Dce(), dcom.WithIPID(ipid)) - if err != nil { - log.Error().Err(err).Msg("Failed to create services client") - return fmt.Errorf("create IWbemServices client: %w", err) - } + if err != nil { + log.Error().Err(err).Msg("Failed to create services client") + return fmt.Errorf("create IWbemServices client: %w", err) + } - log.Info().Msg("Initialized services client") + log.Info().Msg("Initialized services client") - return + return } func (m *Wmi) query(ctx context.Context, class, method string, values map[string]any) (map[string]any, error) { - if m.servicesClient == nil { - return nil, errors.New("module has not been initialized") - } - if out, err := query.NewBuilder(ctx, m.servicesClient, ComVersion). - Spawn(class). // The class to instantiate (i.e., Win32_Process) - Method(method). // The method to call (i.e., Create) - Values(values). // The values to pass to method - Exec(). - Object(); err == nil { - return out.Values(), err - } else { - return nil, fmt.Errorf("spawn WMI query: %w", err) - } + if m.servicesClient == nil { + return nil, errors.New("module has not been initialized") + } + if out, err := query.NewBuilder(ctx, m.servicesClient, ComVersion). + Spawn(class). // The class to instantiate (i.e., Win32_Process) + Method(method). // The method to call (i.e., Create) + Values(values). // The values to pass to method + Exec(). + Object(); err == nil { + return out.Values(), err + } else { + return nil, fmt.Errorf("spawn WMI query: %w", err) + } } diff --git a/pkg/goexec/wmi/proc.go b/pkg/goexec/wmi/proc.go index fe22bdf..abc4bd7 100644 --- a/pkg/goexec/wmi/proc.go +++ b/pkg/goexec/wmi/proc.go @@ -1,59 +1,59 @@ package wmiexec import ( - "context" - "errors" - "github.com/FalconOpsLLC/goexec/pkg/goexec" - "github.com/rs/zerolog" + "context" + "errors" + "github.com/FalconOpsLLC/goexec/pkg/goexec" + "github.com/rs/zerolog" ) const ( - MethodProc = "Proc" + MethodProc = "Proc" ) type WmiProc struct { - Wmi - IO goexec.ExecutionIO - WorkingDirectory string + Wmi + IO goexec.ExecutionIO + WorkingDirectory string } func (m *WmiProc) Execute(ctx context.Context, execIO *goexec.ExecutionIO) (err error) { - log := zerolog.Ctx(ctx).With(). - Str("module", ModuleName). - Str("method", MethodProc). - Logger() - ctx = log.WithContext(ctx) + log := zerolog.Ctx(ctx).With(). + Str("module", ModuleName). + Str("method", MethodProc). + Logger() + ctx = log.WithContext(ctx) - if execIO == nil { - return errors.New("execution IO is nil") - } + if execIO == nil { + return errors.New("execution IO is nil") + } - out, err := m.query(ctx, - "Win32_Process", - "Create", - map[string]any{ - "CommandLine": execIO.String(), - "WorkingDir": m.WorkingDirectory, - }, - ) - if err != nil { - return - } + out, err := m.query(ctx, + "Win32_Process", + "Create", + map[string]any{ + "CommandLine": execIO.String(), + "WorkingDir": m.WorkingDirectory, + }, + ) + if err != nil { + return + } - if pid, ok := out["ProcessId"].(uint32); pid != 0 { - log = log.With().Uint32("pid", pid).Logger() + if pid, ok := out["ProcessId"].(uint32); pid != 0 { + log = log.With().Uint32("pid", pid).Logger() - } else if !ok { - return errors.New("process creation failed") - } - log.Info().Err(err).Msg("Process created") + } else if !ok { + return errors.New("process creation failed") + } + log.Info().Err(err).Msg("Process created") - if ret, ok := out["ReturnValue"].(uint32); ret != 0 { - log.Error().Err(err).Uint32("return", ret).Msg("Process returned non-zero exit code") + if ret, ok := out["ReturnValue"].(uint32); ret != 0 { + log.Error().Err(err).Uint32("return", ret).Msg("Process returned non-zero exit code") - } else if !ok { - return errors.New("invalid call response") - } - return + } else if !ok { + return errors.New("invalid call response") + } + return }