From ab141f2076b141bf885f56cb5730252cc2880041 Mon Sep 17 00:00:00 2001 From: Bryan McNulty Date: Mon, 10 Mar 2025 06:45:04 -0500 Subject: Fix `scmr delete` --- TODO.md | 2 ++ cmd/scmr.go | 46 ++++++++++++++++++++++++++------------------ internal/exec/scmr/exec.go | 17 ++++++++++------ internal/exec/scmr/module.go | 6 ++++++ 4 files changed, 46 insertions(+), 25 deletions(-) diff --git a/TODO.md b/TODO.md index 4b2c54f..610751f 100644 --- a/TODO.md +++ b/TODO.md @@ -18,10 +18,12 @@ - [ ] Add psexec module (RemComSvc) - [ ] Add support for dynamic service executable (of course) + ### Other - [ ] Fix SCMR `change` method so that dependencies field isn't permanently overwritten - [ ] Add `delete` command to all modules that may involve cleanup - use `tsch delete` for reference + - [X] `scmr delete` - [ ] Standardize modules to interface for future use - [ ] Add command to tsch - update task if it already exists. See https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsch/849c131a-64e4-46ef-b015-9d4c599c5167 (`flags` argument) - [ ] Add proxy support - see https://github.com/oiweiwei/go-msrpc/issues/21 diff --git a/cmd/scmr.go b/cmd/scmr.go index 37b52eb..9df9ef1 100644 --- a/cmd/scmr.go +++ b/cmd/scmr.go @@ -5,6 +5,7 @@ import ( "github.com/FalconOpsLLC/goexec/internal/util" "github.com/FalconOpsLLC/goexec/internal/windows" "github.com/RedTeamPentesting/adauth" + "github.com/oiweiwei/go-msrpc/dcerpc" "github.com/spf13/cobra" scmrexec "github.com/FalconOpsLLC/goexec/internal/exec/scmr" @@ -12,48 +13,51 @@ import ( func scmrCmdInit() { registerRpcFlags(scmrCmd) - scmrCmd.PersistentFlags().StringVarP(&executablePath, "executable-path", "f", "", "Full path to remote Windows executable") - scmrCmd.PersistentFlags().StringVarP(&executableArgs, "args", "a", "", "Arguments to pass to executable") - scmrCmd.PersistentFlags().StringVarP(&scmrServiceName, "service-name", "s", "", "Name of service to create or modify") - - if err := scmrCmd.MarkPersistentFlagRequired("executable-path"); err != nil { - panic(err) - } scmrCreateCmdInit() - scmrCmd.AddCommand(scmrChangeCmd) - scmrChangeCmdInit() scmrCmd.AddCommand(scmrCreateCmd) + scmrChangeCmdInit() + scmrCmd.AddCommand(scmrChangeCmd) scmrDeleteCmdInit() scmrCmd.AddCommand(scmrDeleteCmd) } func scmrCreateCmdInit() { + scmrCreateCmd.Flags().StringVarP(&scmrDisplayName, "display-name", "n", "", "Display name of service to create") scmrCreateCmd.Flags().StringVarP(&scmrServiceName, "service-name", "s", "", "Name of service to create") scmrCreateCmd.Flags().BoolVar(&scmrNoDelete, "no-delete", false, "Don't delete service after execution") + scmrCreateCmd.Flags().BoolVar(&scmrNoStart, "no-start", false, "Don't start service") + scmrCreateCmd.Flags().StringVarP(&executablePath, "executable-path", "f", "", "Full path to a remote Windows executable file") + scmrCreateCmd.Flags().StringVarP(&executableArgs, "args", "a", "", "Arguments to pass to the executable") + if err := scmrCreateCmd.MarkFlagRequired("executable-path"); err != nil { + panic(err) + } } func scmrChangeCmdInit() { scmrChangeCmd.Flags().StringVarP(&scmrDisplayName, "display-name", "n", "", "Display name of service to create") scmrChangeCmd.Flags().BoolVar(&scmrNoStart, "no-start", false, "Don't start service") scmrChangeCmd.Flags().StringVarP(&scmrServiceName, "service-name", "s", "", "Name of service to modify") + scmrChangeCmd.Flags().StringVarP(&executablePath, "executable-path", "f", "", "Full path to remote Windows executable") + scmrChangeCmd.Flags().StringVarP(&executableArgs, "args", "a", "", "Arguments to pass to executable") if err := scmrChangeCmd.MarkFlagRequired("service-name"); err != nil { panic(err) } } func scmrDeleteCmdInit() { - scmrDeleteCmd.Flags().StringVarP(&scmrServiceName, "service-name", "s", "", "Name of service to delete") - if err := scmrChangeCmd.MarkFlagRequired("service-name"); err != nil { + scmrDeleteCmd.Flags().StringArrayVarP(&scmrServiceNames, "service-name", "s", scmrServiceNames, "Name of service(s) to delete") + if err := scmrDeleteCmd.MarkFlagRequired("service-name"); err != nil { panic(err) } } var ( // scmr arguments - scmrServiceName string - scmrDisplayName string - scmrNoDelete bool - scmrNoStart bool + scmrServiceName string + scmrServiceNames []string + scmrDisplayName string + scmrNoDelete bool + scmrNoStart bool creds *adauth.Credential target *adauth.Target @@ -90,7 +94,8 @@ References: executor := scmrexec.Module{} cleanCfg := &exec.CleanupConfig{ - CleanupMethod: scmrexec.CleanupMethodDelete, + CleanupMethod: scmrexec.CleanupMethodDelete, + CleanupMethodConfig: scmrexec.CleanupMethodDeleteConfig{}, } connCfg := &exec.ConnectionConfig{ ConnectionMethod: exec.ConnectionMethodDCE, @@ -137,7 +142,8 @@ References: executor := scmrexec.Module{} cleanCfg := &exec.CleanupConfig{ - CleanupMethod: scmrexec.CleanupMethodRevert, + CleanupMethod: scmrexec.CleanupMethodRevert, + CleanupMethodConfig: scmrexec.CleanupMethodRevertConfig{}, } connCfg := &exec.ConnectionConfig{ ConnectionMethod: exec.ConnectionMethodDCE, @@ -177,14 +183,16 @@ References: Use: "delete [target]", Short: "Delete an existing Windows service", Long: `Description: - +TODO `, Args: needsRpcTarget("cifs"), Run: func(cmd *cobra.Command, args []string) { + dceConfig.DceOptions = append(dceConfig.DceOptions, dcerpc.WithInsecure()) executor := scmrexec.Module{} cleanCfg := &exec.CleanupConfig{ - CleanupMethod: scmrexec.CleanupMethodDelete, + CleanupMethod: scmrexec.CleanupMethodDelete, + CleanupMethodConfig: scmrexec.CleanupMethodDeleteConfig{ServiceNames: scmrServiceNames}, } connCfg := &exec.ConnectionConfig{ ConnectionMethod: exec.ConnectionMethodDCE, diff --git a/internal/exec/scmr/exec.go b/internal/exec/scmr/exec.go index 588c580..7134df0 100644 --- a/internal/exec/scmr/exec.go +++ b/internal/exec/scmr/exec.go @@ -70,9 +70,14 @@ func (mod *Module) Cleanup(ctx context.Context, ccfg *exec.CleanupConfig) (err e Str("func", "Cleanup").Logger() if len(mod.services) == 0 { - return nil + if cfg, ok := ccfg.CleanupMethodConfig.(CleanupMethodDeleteConfig); ok && len(cfg.ServiceNames) > 0 { + for _, svcName := range cfg.ServiceNames { + if svcName != "" { + mod.services = append(mod.services, remoteService{name: svcName}) + } + } + } } - if mod.dce == nil || mod.ctl == nil { // Try to reconnect if err := mod.reconnect(ctx); err != nil { @@ -88,11 +93,11 @@ func (mod *Module) Cleanup(ctx context.Context, ccfg *exec.CleanupConfig) (err e DatabaseName: "ServicesActive\x00", DesiredAccess: ServiceAllAccess, // TODO: Replace }); err != nil { - log.Error().Err(err).Msg("Failed to reopen an SCM handle") + log.Error().Err(err).Msg("Failed to open an SCM handle") return err } else { mod.scm = resp.SCM - log.Info().Msg("Reopened SCM handle") + log.Info().Msg("Opened an SCM handle") } } @@ -106,12 +111,12 @@ func (mod *Module) Cleanup(ctx context.Context, ccfg *exec.CleanupConfig) (err e ServiceName: rsvc.name, DesiredAccess: windows.SERVICE_DELETE | windows.SERVICE_CHANGE_CONFIG, }); err != nil { - log.Error().Err(err).Msg("Failed to reopen a service handle") + log.Error().Err(err).Msg("Failed to open a service handle") continue } else { rsvc.handle = or.Service } - log.Info().Msg("Service handle reopened") + log.Info().Msg("Service handle opened") } if ccfg.CleanupMethod == CleanupMethodDelete { // Delete the service diff --git a/internal/exec/scmr/module.go b/internal/exec/scmr/module.go index 2a2d378..0372668 100644 --- a/internal/exec/scmr/module.go +++ b/internal/exec/scmr/module.go @@ -36,3 +36,9 @@ type MethodChangeConfig struct { NoStart bool ServiceName string } + +type CleanupMethodDeleteConfig struct { + ServiceNames []string +} + +type CleanupMethodRevertConfig struct{} -- cgit v1.2.3