diff options
author | Bryan McNulty <bryanmcnulty@protonmail.com> | 2025-04-26 05:12:13 -0500 |
---|---|---|
committer | Bryan McNulty <bryanmcnulty@protonmail.com> | 2025-04-26 05:12:13 -0500 |
commit | 9d2a16e16d21bcfed1da89001b095776261d790b (patch) | |
tree | 1a48264072a8fcd0ad2ae78d96db53b7ff91d727 | |
parent | e860eced799e64ebff56012a7655155d26b3a2a2 (diff) | |
download | goexec-9d2a16e16d21bcfed1da89001b095776261d790b.tar.gz goexec-9d2a16e16d21bcfed1da89001b095776261d790b.zip |
Fixed some linter issues
-rw-r--r-- | cmd/dcom.go | 110 | ||||
-rw-r--r-- | cmd/root.go | 506 | ||||
-rw-r--r-- | pkg/goexec/scmr/scmr.go | 8 | ||||
-rw-r--r-- | pkg/goexec/tsch/task/action.go | 2 | ||||
-rw-r--r-- | pkg/goexec/wmi/module.go | 236 |
5 files changed, 430 insertions, 432 deletions
diff --git a/cmd/dcom.go b/cmd/dcom.go index 89fac18..1a8d031 100644 --- a/cmd/dcom.go +++ b/cmd/dcom.go @@ -1,80 +1,80 @@ 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() + cmdFlags[dcomCmd] = []*flagSet{ + defaultAuthFlags, + defaultLogFlags, + defaultNetRpcFlags, + } + dcomMmcCmdInit() - dcomCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags) - dcomCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags) - dcomCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags) - dcomCmd.AddCommand(dcomMmcCmd) + dcomCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags) + dcomCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags) + dcomCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags) + dcomCmd.AddCommand(dcomMmcCmd) } 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, - } + cmdFlags[dcomMmcCmd] = []*flagSet{ + dcomMmcExecFlags, + defaultAuthFlags, + defaultLogFlags, + defaultNetRpcFlags, + } - dcomMmcCmd.Flags().AddFlagSet(dcomMmcExecFlags.Flags) + dcomMmcCmd.Flags().AddFlagSet(dcomMmcExecFlags.Flags) } var ( - dcomMmc dcomexec.DcomMmc + dcomMmc dcomexec.DcomMmc - 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 DCOM MMC20.Application object", - Long: `Description: + dcomMmcCmd = &cobra.Command{ + Use: "mmc [target]", + Short: "Execute with the DCOM MMC20.Application 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"), - ), - Run: func(cmd *cobra.Command, args []string) { - dcomMmc.Dcom.Client = &rpcClient - dcomMmc.IO = exec + Args: args( + argsRpcClient("host"), + argsOutput("smb"), + ), + Run: func(cmd *cobra.Command, args []string) { + dcomMmc.Client = &rpcClient + dcomMmc.IO = exec - ctx := log.With(). - Str("module", "dcom"). - Str("method", "mmc"). - Logger().WithContext(gssapi.NewSecurityContext(context.Background())) + ctx := log.With(). + Str("module", "dcom"). + Str("method", "mmc"). + 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") + } + }, + } ) diff --git a/cmd/root.go b/cmd/root.go index 96173a4..12f3e19 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,26 +1,26 @@ package cmd import ( - "fmt" - "github.com/FalconOpsLLC/goexec/pkg/goexec" - "github.com/FalconOpsLLC/goexec/pkg/goexec/dce" - "github.com/FalconOpsLLC/goexec/pkg/goexec/smb" - "github.com/RedTeamPentesting/adauth" - "github.com/google/uuid" - "github.com/oiweiwei/go-msrpc/ssp" - "github.com/oiweiwei/go-msrpc/ssp/gssapi" - "github.com/rs/zerolog" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "golang.org/x/term" - "io" - "os" - "runtime/pprof" + "fmt" + "github.com/FalconOpsLLC/goexec/pkg/goexec" + "github.com/FalconOpsLLC/goexec/pkg/goexec/dce" + "github.com/FalconOpsLLC/goexec/pkg/goexec/smb" + "github.com/RedTeamPentesting/adauth" + "github.com/google/uuid" + "github.com/oiweiwei/go-msrpc/ssp" + "github.com/oiweiwei/go-msrpc/ssp/gssapi" + "github.com/rs/zerolog" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "golang.org/x/term" + "io" + "os" + "runtime/pprof" ) type flagSet struct { - Label string - Flags *pflag.FlagSet + Label string + Flags *pflag.FlagSet } const helpTemplate = `Usage:{{if .Runnable}} @@ -52,55 +52,55 @@ Use "{{.CommandPath}} [command] --help" for more information about a command.{{e ` var ( - cmdFlags = make(map[*cobra.Command][]*flagSet) - - defaultAuthFlags, defaultLogFlags, defaultNetRpcFlags *flagSet - - returnCode int - toClose []io.Closer - - // === IO === - stageFilePath string - outputMethod string - outputPath string - // ========== - - // === Logging === - logJson bool // Log output in JSON lines - logDebug bool // Output debug log messages - logQuiet bool // Suppress logging output - logOutput string // Log output file - logLevel zerolog.Level = zerolog.InfoLevel - logFile io.WriteCloser = os.Stderr - log zerolog.Logger - // =============== - - // === Network === - proxy string - rpcClient dce.Client - smbClient smb.Client - // =============== - - // === Resource profiling === - cpuProfile string - memProfile string - cpuProfileFile io.WriteCloser - memProfileFile io.WriteCloser - // ========================== - - exec = goexec.ExecutionIO{ - Input: new(goexec.ExecutionInput), - Output: new(goexec.ExecutionOutput), - } - - adAuthOpts *adauth.Options - credential *adauth.Credential - target *adauth.Target - - rootCmd = &cobra.Command{ - Use: "goexec", - Short: `goexec - Windows remote execution multitool`, - Long: ` + cmdFlags = make(map[*cobra.Command][]*flagSet) + + defaultAuthFlags, defaultLogFlags, defaultNetRpcFlags *flagSet + + returnCode int + toClose []io.Closer + + // === IO === + stageFilePath string + outputMethod string + outputPath string + // ========== + + // === Logging === + logJson bool // Log output in JSON lines + logDebug bool // Output debug log messages + logQuiet bool // Suppress logging output + logOutput string // Log output file + logLevel = zerolog.InfoLevel + logFile io.WriteCloser = os.Stderr + log zerolog.Logger + // =============== + + // === Network === + proxy string + rpcClient dce.Client + smbClient smb.Client + // =============== + + // === Resource profiling === + cpuProfile string + memProfile string + cpuProfileFile io.WriteCloser + memProfileFile io.WriteCloser + // ========================== + + exec = goexec.ExecutionIO{ + Input: new(goexec.ExecutionInput), + Output: new(goexec.ExecutionOutput), + } + + adAuthOpts *adauth.Options + credential *adauth.Credential + target *adauth.Target + + rootCmd = &cobra.Command{ + Use: "goexec", + Short: `goexec - Windows remote execution multitool`, + Long: ` ___ ___ ___ _ _ ___ ___ | . | . | -_|_'_| -_| _| |_ |___|___|_,_|___|___| @@ -113,200 +113,200 @@ Authors: FalconOps LLC (@FalconOpsLLC), while providing an extremely flexible CLI and a strong focus on OPSEC. `, - PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) { - - // Parse logging options - { - if logOutput != "" { - logFile, err = os.OpenFile(logOutput, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) - if err != nil { - return - } - toClose = append(toClose, logFile) - logJson = true - } - if logQuiet { - logLevel = zerolog.ErrorLevel - } else if logDebug { - logLevel = zerolog.DebugLevel - } - if logJson { - log = zerolog.New(logFile).With().Timestamp().Logger() - } else { - log = zerolog.New(zerolog.ConsoleWriter{Out: logFile}).With().Timestamp().Logger() - } - log = log.Level(logLevel) - } - - // CPU / memory profiling - { - if cpuProfile != "" { - if cpuProfileFile, err = os.OpenFile(cpuProfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil { - log.Error().Err(err).Msg("Failed to open CPU profile for writing") - return - } - toClose = append(toClose, cpuProfileFile) - - if err = pprof.StartCPUProfile(cpuProfileFile); err != nil { - log.Error().Err(err).Msg("Failed to start CPU profile") - return - } - } - if memProfile != "" { - if memProfileFile, err = os.OpenFile(memProfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil { - log.Error().Err(err).Msg("Failed to open memory profile for writing") - return - } - toClose = append(toClose, memProfileFile) - } - } - - if proxy != "" { - rpcClient.Proxy = proxy - smbClient.Proxy = proxy - } - - if outputPath != "" { - if outputMethod == "smb" { - if exec.Output.RemotePath == "" { - exec.Output.RemotePath = `C:\Windows\Temp\` + uuid.NewString() - } - exec.Output.Provider = &smb.OutputFileFetcher{ - Client: &smbClient, - Share: `ADMIN$`, // TODO: dynamic - SharePath: `C:\Windows`, - File: exec.Output.RemotePath, - DeleteOutputFile: !exec.Output.NoDelete, - } - } - } - return - }, - - PersistentPostRun: func(cmd *cobra.Command, args []string) { - - if memProfileFile != nil { - if err := pprof.WriteHeapProfile(memProfileFile); err != nil { - log.Error().Err(err).Msg("Failed to write memory profile") - return - } - } - - if cpuProfileFile != nil { - pprof.StopCPUProfile() - } - - for _, c := range toClose { - if c != nil { - if err := c.Close(); err != nil { - log.Warn().Err(err).Msg("Failed to close stream") - } - } - } - - if exec.Input != nil && exec.Input.StageFile != nil { - if err := exec.Input.StageFile.Close(); err != nil { - // ... - } - } - }, - } + PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) { + + // Parse logging options + { + if logOutput != "" { + logFile, err = os.OpenFile(logOutput, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return + } + toClose = append(toClose, logFile) + logJson = true + } + if logQuiet { + logLevel = zerolog.ErrorLevel + } else if logDebug { + logLevel = zerolog.DebugLevel + } + if logJson { + log = zerolog.New(logFile).With().Timestamp().Logger() + } else { + log = zerolog.New(zerolog.ConsoleWriter{Out: logFile}).With().Timestamp().Logger() + } + log = log.Level(logLevel) + } + + // CPU / memory profiling + { + if cpuProfile != "" { + if cpuProfileFile, err = os.OpenFile(cpuProfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil { + log.Error().Err(err).Msg("Failed to open CPU profile for writing") + return + } + toClose = append(toClose, cpuProfileFile) + + if err = pprof.StartCPUProfile(cpuProfileFile); err != nil { + log.Error().Err(err).Msg("Failed to start CPU profile") + return + } + } + if memProfile != "" { + if memProfileFile, err = os.OpenFile(memProfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil { + log.Error().Err(err).Msg("Failed to open memory profile for writing") + return + } + toClose = append(toClose, memProfileFile) + } + } + + if proxy != "" { + rpcClient.Proxy = proxy + smbClient.Proxy = proxy + } + + if outputPath != "" { + if outputMethod == "smb" { + if exec.Output.RemotePath == "" { + exec.Output.RemotePath = `C:\Windows\Temp\` + uuid.NewString() + } + exec.Output.Provider = &smb.OutputFileFetcher{ + Client: &smbClient, + Share: `ADMIN$`, // TODO: dynamic + SharePath: `C:\Windows`, + File: exec.Output.RemotePath, + DeleteOutputFile: !exec.Output.NoDelete, + } + } + } + return + }, + + PersistentPostRun: func(cmd *cobra.Command, args []string) { + + if memProfileFile != nil { + if err := pprof.WriteHeapProfile(memProfileFile); err != nil { + log.Error().Err(err).Msg("Failed to write memory profile") + return + } + } + + if cpuProfileFile != nil { + pprof.StopCPUProfile() + } + + for _, c := range toClose { + if c != nil { + if err := c.Close(); err != nil { + log.Warn().Err(err).Msg("Failed to close stream") + } + } + } + + if exec.Input != nil && exec.Input.StageFile != nil { + if err := exec.Input.StageFile.Close(); err != nil { + // ... + } + } + }, + } ) func newFlagSet(name string) *flagSet { - flags := pflag.NewFlagSet(name, pflag.ExitOnError) - flags.SortFlags = false - return &flagSet{ - Label: name, - Flags: flags, - } + flags := pflag.NewFlagSet(name, pflag.ExitOnError) + flags.SortFlags = false + return &flagSet{ + Label: name, + Flags: flags, + } } func init() { - // Auth init - { - gssapi.AddMechanism(ssp.SPNEGO) - gssapi.AddMechanism(ssp.NTLM) - gssapi.AddMechanism(ssp.KRB5) - } - - // CPU / Memory profiling - { - rootCmd.PersistentFlags().StringVar(&cpuProfile, "cpu-profile", "", "Write CPU profile to `file`") - rootCmd.PersistentFlags().StringVar(&memProfile, "mem-profile", "", "Write memory profile to `file`") - - if err := rootCmd.PersistentFlags().MarkHidden("cpu-profile"); err != nil { - panic(err) - } - if err := rootCmd.PersistentFlags().MarkHidden("mem-profile"); err != nil { - panic(err) - } - } - - // Cobra init - { - cobra.EnableCommandSorting = false - { - defaultNetRpcFlags = newFlagSet("Network") - registerNetworkFlags(defaultNetRpcFlags.Flags) - } - { - defaultLogFlags = newFlagSet("Logging") - registerLoggingFlags(defaultLogFlags.Flags) - } - { - defaultAuthFlags = newFlagSet("Authentication") - adAuthOpts = &adauth.Options{ - Debug: log.Debug().Msgf, - } - adAuthOpts.RegisterFlags(defaultAuthFlags.Flags) - } - - modules := &cobra.Group{ - ID: "module", - Title: "Execution Commands:", - } - rootCmd.AddGroup(modules) - - cmdFlags[rootCmd] = []*flagSet{ - defaultLogFlags, - defaultAuthFlags, - } - - cobra.AddTemplateFunc("flags", func(fs *pflag.FlagSet) string { - if width, _, err := term.GetSize(int(os.Stdout.Fd())); err == nil { - return fs.FlagUsagesWrapped(width - 1) - } - return fs.FlagUsagesWrapped(80 - 1) - }) - - cobra.AddTemplateFunc("cmdFlags", func(cmd *cobra.Command) []*flagSet { - return cmdFlags[cmd] - }) - - rootCmd.InitDefaultVersionFlag() - rootCmd.InitDefaultHelpCmd() - rootCmd.SetHelpTemplate("{{if (ne .Long \"\")}}{{.Long}}\n\n{{end}}" + helpTemplate) - rootCmd.SetUsageTemplate(helpTemplate) - - // Modules init - { - dcomCmdInit() - rootCmd.AddCommand(dcomCmd) - wmiCmdInit() - rootCmd.AddCommand(wmiCmd) - scmrCmdInit() - rootCmd.AddCommand(scmrCmd) - tschCmdInit() - rootCmd.AddCommand(tschCmd) - } - } + // Auth init + { + gssapi.AddMechanism(ssp.SPNEGO) + gssapi.AddMechanism(ssp.NTLM) + gssapi.AddMechanism(ssp.KRB5) + } + + // CPU / Memory profiling + { + rootCmd.PersistentFlags().StringVar(&cpuProfile, "cpu-profile", "", "Write CPU profile to `file`") + rootCmd.PersistentFlags().StringVar(&memProfile, "mem-profile", "", "Write memory profile to `file`") + + if err := rootCmd.PersistentFlags().MarkHidden("cpu-profile"); err != nil { + panic(err) + } + if err := rootCmd.PersistentFlags().MarkHidden("mem-profile"); err != nil { + panic(err) + } + } + + // Cobra init + { + cobra.EnableCommandSorting = false + { + defaultNetRpcFlags = newFlagSet("Network") + registerNetworkFlags(defaultNetRpcFlags.Flags) + } + { + defaultLogFlags = newFlagSet("Logging") + registerLoggingFlags(defaultLogFlags.Flags) + } + { + defaultAuthFlags = newFlagSet("Authentication") + adAuthOpts = &adauth.Options{ + Debug: log.Debug().Msgf, + } + adAuthOpts.RegisterFlags(defaultAuthFlags.Flags) + } + + modules := &cobra.Group{ + ID: "module", + Title: "Execution Commands:", + } + rootCmd.AddGroup(modules) + + cmdFlags[rootCmd] = []*flagSet{ + defaultLogFlags, + defaultAuthFlags, + } + + cobra.AddTemplateFunc("flags", func(fs *pflag.FlagSet) string { + if width, _, err := term.GetSize(int(os.Stdout.Fd())); err == nil { + return fs.FlagUsagesWrapped(width - 1) + } + return fs.FlagUsagesWrapped(80 - 1) + }) + + cobra.AddTemplateFunc("cmdFlags", func(cmd *cobra.Command) []*flagSet { + return cmdFlags[cmd] + }) + + rootCmd.InitDefaultVersionFlag() + rootCmd.InitDefaultHelpCmd() + rootCmd.SetHelpTemplate("{{if (ne .Long \"\")}}{{.Long}}\n\n{{end}}" + helpTemplate) + rootCmd.SetUsageTemplate(helpTemplate) + + // Modules init + { + dcomCmdInit() + rootCmd.AddCommand(dcomCmd) + wmiCmdInit() + rootCmd.AddCommand(wmiCmd) + scmrCmdInit() + rootCmd.AddCommand(scmrCmd) + tschCmdInit() + rootCmd.AddCommand(tschCmd) + } + } } func Execute() { - if err := rootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(1) - } - os.Exit(returnCode) + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } + os.Exit(returnCode) } diff --git a/pkg/goexec/scmr/scmr.go b/pkg/goexec/scmr/scmr.go index 4831ac7..696415b 100644 --- a/pkg/goexec/scmr/scmr.go +++ b/pkg/goexec/scmr/scmr.go @@ -42,10 +42,10 @@ const ( SERVICE_STOPPED uint32 = 0x00000001 */ - ServiceDeleteAccess uint32 = ServiceDelete - ServiceModifyAccess uint32 = ServiceQueryConfig | ServiceChangeConfig | ServiceStop | ServiceStart | ServiceDelete - ServiceCreateAccess uint32 = ScManagerCreateService | ServiceStart | ServiceStop | ServiceDelete - ServiceAllAccess uint32 = ServiceCreateAccess | ServiceModifyAccess + ServiceDeleteAccess = ServiceDelete + ServiceModifyAccess = ServiceQueryConfig | ServiceChangeConfig | ServiceStop | ServiceStart | ServiceDelete + ServiceCreateAccess = ScManagerCreateService | ServiceStart | ServiceStop | ServiceDelete + ServiceAllAccess = ServiceCreateAccess | ServiceModifyAccess ) type service struct { diff --git a/pkg/goexec/tsch/task/action.go b/pkg/goexec/tsch/task/action.go index de6c29f..903c633 100644 --- a/pkg/goexec/tsch/task/action.go +++ b/pkg/goexec/tsch/task/action.go @@ -85,7 +85,7 @@ type ShowMessageAction struct { // NamedValues holds zero or more <Value name="…">…</Value> entries. type NamedValues struct { - XMLName xml.Name `xml:"HeaderFields"` + XMLName xml.Name //`xml:"HeaderFields"` Value []NamedValue `xml:"Value"` } diff --git a/pkg/goexec/wmi/module.go b/pkg/goexec/wmi/module.go index 90bbaa8..0aedd7e 100644 --- a/pkg/goexec/wmi/module.go +++ b/pkg/goexec/wmi/module.go @@ -1,141 +1,139 @@ 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" ) 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() - - 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) - } - - 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") - - var newOpts []dcerpc.Option - - for _, bind := range actResponse.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 = 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) - } - - 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)) - - 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, - }) - - log.Info().Msg("Completed NTLMLogin operation") - - if err != nil { - log.Error().Err(err).Msg("Failed to login on remote instance") - return fmt.Errorf("login: IWbemLevel1Login::NTLMLogin: %w", err) - } - - 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) - } - - log.Info().Msg("Initialized services client") - - return + log := zerolog.Ctx(ctx).With(). + Str("module", ModuleName).Logger() + + 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) + } + + 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") + + var newOpts []dcerpc.Option + + for _, bind := range actResponse.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 = 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) + } + + 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)) + + 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, + }) + + log.Info().Msg("Completed NTLMLogin operation") + + if err != nil { + log.Error().Err(err).Msg("Failed to login on remote instance") + return fmt.Errorf("login: IWbemLevel1Login::NTLMLogin: %w", err) + } + + 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) + } + + log.Info().Msg("Initialized services client") + + return } -func (m *Wmi) query(ctx context.Context, class, method string, values map[string]any) (outValues map[string]any, err error) { - outValues = make(map[string]any) - - 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) - } +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) + } } |