diff --git a/TODO.md b/TODO.md deleted file mode 100644 index d0e32d4..0000000 --- a/TODO.md +++ /dev/null @@ -1,80 +0,0 @@ -# TODO - -We wanted to make development of this project as transparent as possible, so we've included our development TODOs in this file. - -## TSCH - -- [X] Clean up TSCH module -- [X] Session hijacking -- [X] Generate random name/path -- [X] Output -- [X] Add `tsch change` -- [ ] Serialize XML with default indent level? - -### `tsch change` - -- [ ] Add `--session` (like `tsch demand`) -- [ ] Add the option to avoid direct task start using TimeTrigger (similar to `tsch create`) - -## SCMR - -- [X] Clean up SCMR module -- [X] add dynamic string binding support -- [X] general cleanup. Use TSCH & WMI as reference -- [ ] Output - -## DCOM - -- [X] Add DCOM module -- [X] MMC20.Application method -- [X] Output - -## WMI - -- [X] Add WMI module -- [X] Clean up WMI module -- [X] Output -- [ ] WMI `reg` subcommand - read & edit the registry -- [ ] File transfer functionality - -## Other - -- [X] Add proxy support - see https://github.com/oiweiwei/go-msrpc/issues/21 -- [X] README -- [X] pprof integration (hidden flag(s)) -- [X] Descriptions for all modules and methods -- [ ] Add SMB file transfer interface - -## Bugs - -- [X] (Fixed) SMB transport for SCMR module - `rpc_s_cannot_support: The requested operation is not supported.` -- [X] (Fixed) Proxy - EPM doesn't use the proxy dialer -- [X] (Fixed) Kerberos requests don't dial through proxy -- [X] (Fixed) Panic when closing nil log file -- [X] `scmr change` doesn't revert service cmdline -- [X] Fix SCMR `change` method so that dependencies field isn't permanently overwritten - -## Lower Priority - -- [ ] `--shell` option -- [ ] Add Go tests -- [ ] ability to specify multiple targets - -### TSCH - -- [ ] Add more trigger types - -### SCMR - -- [ ] `psexec` with PsExeSVC.exe AND NOT Impacket's RemCom build - https://sensepost.com/blog/2025/psexecing-the-right-way-and-why-zero-trust-is-mandatory/ - -### DCOM - -- [X] ShellWindows -- [ ] ShellBrowserWindow - -### WinRM - -- [ ] Add basic WinRM module - https://github.com/bryanmcnulty/winrm - - [ ] File transfer functionality - - [ ] Shell functionality \ No newline at end of file diff --git a/cmd/args.go b/cmd/args.go index d517a4f..d7fd13d 100644 --- a/cmd/args.go +++ b/cmd/args.go @@ -1,34 +1,42 @@ package cmd import ( - "context" - "encoding/json" - "errors" - "fmt" - "os" + "context" + "encoding/json" + "errors" + "fmt" + "os" - "github.com/spf13/cobra" - "github.com/spf13/pflag" + "github.com/spf13/cobra" + "github.com/spf13/pflag" ) 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, "Don't use EPM to discover 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, "Don't use EPM to discover RPC endpoints") + fs.BoolVar(&rpcClient.UseEpm, "epm", false, "Use EPM to discover available bindings") + 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") + if err := fs.MarkHidden("no-epm"); err != nil { + panic(err) + } + if err := fs.MarkDeprecated("no-epm", "use --epm=false instead"); err != nil { + panic(err) + } + + //cmd.MarkFlagsMutuallyExclusive("endpoint", "epm-filter") + //cmd.MarkFlagsMutuallyExclusive("no-epm", "epm-filter") } // FUTURE: automatically stage & execute file @@ -40,127 +48,126 @@ 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, endpoint string) func(cmd *cobra.Command, args []string) error { - return args( - argsTarget(proto), + return args( + argsTarget(proto), - func(cmd *cobra.Command, args []string) (err error) { - if rpcClient.Endpoint == "" { - if endpoint != "" { - rpcClient.Endpoint = endpoint - rpcClient.NoEpm = true - } else { - rpcClient.NoEpm = false - } - } - rpcClient.Target = target - rpcClient.Credential = credential - rpcClient.Proxy = proxy + func(cmd *cobra.Command, args []string) (err error) { + switch { + case rpcClient.Endpoint != "": + case endpoint == "": + rpcClient.UseEpm = true + default: + rpcClient.Endpoint = endpoint + } + 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/pkg/goexec/dce/client.go b/pkg/goexec/dce/client.go index 89c78a8..0d14bd3 100644 --- a/pkg/goexec/dce/client.go +++ b/pkg/goexec/dce/client.go @@ -1,111 +1,111 @@ package dce import ( - "context" - "fmt" + "context" + "fmt" - "github.com/RedTeamPentesting/adauth/smbauth" - "github.com/oiweiwei/go-msrpc/dcerpc" - "github.com/oiweiwei/go-msrpc/msrpc/epm/epm/v3" - msrpcSMB2 "github.com/oiweiwei/go-msrpc/smb2" - "github.com/rs/zerolog" + "github.com/RedTeamPentesting/adauth/smbauth" + "github.com/oiweiwei/go-msrpc/dcerpc" + "github.com/oiweiwei/go-msrpc/msrpc/epm/epm/v3" + msrpcSMB2 "github.com/oiweiwei/go-msrpc/smb2" + "github.com/rs/zerolog" ) type Client struct { - Options + Options - conn dcerpc.Conn + conn dcerpc.Conn } func (c *Client) String() string { - return ClientName + return ClientName } func (c *Client) Reconnect(ctx context.Context, opts ...dcerpc.Option) (err error) { - c.DcerpcOptions = append(c.DcerpcOptions, opts...) + c.DcerpcOptions = append(c.DcerpcOptions, opts...) - return c.Connect(ctx) + return c.Connect(ctx) } func (c *Client) Dce() (dce dcerpc.Conn) { - return c.conn + return c.conn } func (c *Client) Logger(ctx context.Context) (log zerolog.Logger) { - return zerolog.Ctx(ctx).With(). - Str("client", c.String()).Logger() + return zerolog.Ctx(ctx).With(). + Str("client", c.String()).Logger() } func (c *Client) Connect(ctx context.Context) (err error) { - log := c.Logger(ctx) - ctx = log.WithContext(ctx) + log := c.Logger(ctx) + ctx = log.WithContext(ctx) - var do, eo []dcerpc.Option + var do, eo []dcerpc.Option - do = append(do, c.DcerpcOptions...) - do = append(do, c.authOptions...) + do = append(do, c.DcerpcOptions...) + do = append(do, c.authOptions...) - if c.Smb { - var so []msrpcSMB2.DialerOption + if c.Smb { + var so []msrpcSMB2.DialerOption - if !c.NoSign { - so = append(so, msrpcSMB2.WithSign()) - eo = append(eo, dcerpc.WithSign()) - } - if !c.NoSeal { - so = append(so, msrpcSMB2.WithSeal()) - eo = append(eo, dcerpc.WithSeal()) - } + if !c.NoSign { + so = append(so, msrpcSMB2.WithSign()) + eo = append(eo, dcerpc.WithSign()) + } + if !c.NoSeal { + so = append(so, msrpcSMB2.WithSeal()) + eo = append(eo, dcerpc.WithSeal()) + } - if smbDialer, err := smbauth.Dialer(ctx, c.Credential, c.Target, &smbauth.Options{ - SMBOptions: so, - KerberosDialer: c.dialer, - }); err != nil { - return fmt.Errorf("parse smb auth: %w", err) + if smbDialer, err := smbauth.Dialer(ctx, c.Credential, c.Target, &smbauth.Options{ + SMBOptions: so, + KerberosDialer: c.dialer, + }); err != nil { + return fmt.Errorf("parse smb auth: %w", err) - } else { - do = append(do, dcerpc.WithSMBDialer(smbDialer)) - } - } else { + } else { + do = append(do, dcerpc.WithSMBDialer(smbDialer)) + } + } else { - if !c.NoSign { - do = append(do, dcerpc.WithSign()) - eo = append(eo, dcerpc.WithSign()) - } - if !c.NoSeal { - do = append(do, dcerpc.WithSeal()) - eo = append(eo, dcerpc.WithSeal()) - } - } + if !c.NoSign { + do = append(do, dcerpc.WithSign()) + eo = append(eo, dcerpc.WithSign()) + } + if !c.NoSeal { + do = append(do, dcerpc.WithSeal()) + eo = append(eo, dcerpc.WithSeal()) + } + } - if !c.NoLog { - do = append(do, dcerpc.WithLogger(log)) - eo = append(eo, dcerpc.WithLogger(log)) - } + if !c.NoLog { + do = append(do, dcerpc.WithLogger(log)) + eo = append(eo, dcerpc.WithLogger(log)) + } - if !c.NoEpm { - log.Debug().Msg("Using endpoint mapper") + if c.UseEpm && !c.NoEpm { + log.Debug().Msg("Using endpoint mapper") - eo = append(eo, c.EpmOptions...) - eo = append(eo, c.authOptions...) + eo = append(eo, c.EpmOptions...) + eo = append(eo, c.authOptions...) - do = append(do, epm.EndpointMapper(ctx, c.Host, eo...)) - } + do = append(do, epm.EndpointMapper(ctx, c.Host, eo...)) + } - for _, e := range c.stringBindings { - do = append(do, dcerpc.WithEndpoint(e.String())) - } + for _, e := range c.stringBindings { + do = append(do, dcerpc.WithEndpoint(e.String())) + } - if c.conn, err = dcerpc.Dial(ctx, c.Host, do...); err != nil { + if c.conn, err = dcerpc.Dial(ctx, c.Host, do...); err != nil { - log.Error().Err(err).Msgf("Failed to connect to %s endpoint", c.String()) - return fmt.Errorf("dial %s: %w", c.String(), err) - } + log.Error().Err(err).Msgf("Failed to connect to %s endpoint", c.String()) + return fmt.Errorf("dial %s: %w", c.String(), err) + } - return + return } func (c *Client) Close(ctx context.Context) (err error) { - return c.conn.Close(ctx) + return c.conn.Close(ctx) } diff --git a/pkg/goexec/dce/options.go b/pkg/goexec/dce/options.go index 675aab4..f06fc8e 100644 --- a/pkg/goexec/dce/options.go +++ b/pkg/goexec/dce/options.go @@ -1,118 +1,121 @@ package dce import ( - "context" - "fmt" - "net" + "context" + "fmt" + "net" - "github.com/FalconOpsLLC/goexec/pkg/goexec" - "github.com/RedTeamPentesting/adauth" - "github.com/RedTeamPentesting/adauth/dcerpcauth" - "github.com/oiweiwei/go-msrpc/dcerpc" + "github.com/FalconOpsLLC/goexec/pkg/goexec" + "github.com/RedTeamPentesting/adauth" + "github.com/RedTeamPentesting/adauth/dcerpcauth" + "github.com/oiweiwei/go-msrpc/dcerpc" ) type Options struct { - goexec.ClientOptions - goexec.AuthOptions + goexec.ClientOptions + goexec.AuthOptions - // NoSign disables packet signing by omitting dcerpc.WithSign() - NoSign bool `json:"no_sign" yaml:"no_sign"` + // NoSign disables packet signing by omitting dcerpc.WithSign() + NoSign bool `json:"no_sign" yaml:"no_sign"` - // NoSeal disables packet stub encryption by omitting dcerpc.WithSeal() - NoSeal bool `json:"no_seal" yaml:"no_seal"` + // NoSeal disables packet stub encryption by omitting dcerpc.WithSeal() + NoSeal bool `json:"no_seal" yaml:"no_seal"` - // NoLog disables logging by omitting dcerpc.WithLogger(...) - NoLog bool `json:"no_log" yaml:"no_log"` + // NoLog disables logging by omitting dcerpc.WithLogger(...) + NoLog bool `json:"no_log" yaml:"no_log"` - // NoEpm disables DCE endpoint mapper communications - NoEpm bool `json:"no_epm" yaml:"no_epm"` + // UseEpm enables DCE endpoint mapper communications + UseEpm bool `json:"use_epm" yaml:"use_epm"` - // Endpoint stores the explicit DCE string binding to use - Endpoint string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"` + // NoEpm disables DCE endpoint mapper communications + NoEpm bool `json:"no_epm" yaml:"no_epm"` // Deprecated in favor of UseEpm - // Filter stores the filter for returned endpoints from an endpoint mapper - Filter string `json:"filter,omitempty" yaml:"filter,omitempty"` + // Endpoint stores the explicit DCE string binding to use + Endpoint string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"` - // Smb enables SMB transport for DCE/RPC - Smb bool `json:"use_smb" yaml:"use_smb"` + // Filter stores the filter for returned endpoints from an endpoint mapper + Filter string `json:"filter,omitempty" yaml:"filter,omitempty"` - stringBindings []*dcerpc.StringBinding - dialer goexec.Dialer - authOptions []dcerpc.Option - DcerpcOptions []dcerpc.Option - EpmOptions []dcerpc.Option + // Smb enables SMB transport for DCE/RPC + Smb bool `json:"use_smb" yaml:"use_smb"` + + stringBindings []*dcerpc.StringBinding + dialer goexec.Dialer + authOptions []dcerpc.Option + DcerpcOptions []dcerpc.Option + EpmOptions []dcerpc.Option } func (c *Client) Parse(ctx context.Context) (err error) { - // Reset internals - { - c.dialer = nil - c.stringBindings = []*dcerpc.StringBinding{} - c.authOptions = []dcerpc.Option{} - c.DcerpcOptions = []dcerpc.Option{} - c.EpmOptions = []dcerpc.Option{ - dcerpc.WithSign(), // Require signing for EPM - } - } + // Reset internals + { + c.dialer = nil + c.stringBindings = []*dcerpc.StringBinding{} + c.authOptions = []dcerpc.Option{} + c.DcerpcOptions = []dcerpc.Option{} + c.EpmOptions = []dcerpc.Option{ + dcerpc.WithSign(), // Require signing for EPM + } + } - if !c.NoSeal { - // Enable encryption - c.DcerpcOptions = append(c.DcerpcOptions, dcerpc.WithSeal(), dcerpc.WithSecurityLevel(dcerpc.AuthLevelPktPrivacy)) - c.EpmOptions = append(c.EpmOptions, dcerpc.WithSeal(), dcerpc.WithSecurityLevel(dcerpc.AuthLevelPktPrivacy)) - } - if !c.NoSign { - // Enable signing - c.DcerpcOptions = append(c.DcerpcOptions, dcerpc.WithSign()) - } + if !c.NoSeal { + // Enable encryption + c.DcerpcOptions = append(c.DcerpcOptions, dcerpc.WithSeal(), dcerpc.WithSecurityLevel(dcerpc.AuthLevelPktPrivacy)) + c.EpmOptions = append(c.EpmOptions, dcerpc.WithSeal(), dcerpc.WithSecurityLevel(dcerpc.AuthLevelPktPrivacy)) + } + if !c.NoSign { + // Enable signing + c.DcerpcOptions = append(c.DcerpcOptions, dcerpc.WithSign()) + } - // Parse DCERPC endpoint - if c.Endpoint != "" { - sb, err := dcerpc.ParseStringBinding(c.Endpoint) - if err != nil { - return err - } - if sb.ProtocolSequence == dcerpc.ProtocolSequenceNamedPipe { - c.Smb = true - } - c.stringBindings = append(c.stringBindings, sb) - } + // Parse DCERPC endpoint + if c.Endpoint != "" { + sb, err := dcerpc.ParseStringBinding(c.Endpoint) + if err != nil { + return err + } + if sb.ProtocolSequence == dcerpc.ProtocolSequenceNamedPipe { + c.Smb = true + } + c.stringBindings = append(c.stringBindings, sb) + } - // Parse EPM filter - if c.Filter != "" { - sb, err := dcerpc.ParseStringBinding(c.Filter) - if err != nil { - return err - } - if sb.ProtocolSequence == dcerpc.ProtocolSequenceNamedPipe { - c.Smb = true - } - c.stringBindings = append(c.stringBindings, sb) - } + // Parse EPM filter + if c.Filter != "" { + sb, err := dcerpc.ParseStringBinding(c.Filter) + if err != nil { + return err + } + if sb.ProtocolSequence == dcerpc.ProtocolSequenceNamedPipe { + c.Smb = true + } + c.stringBindings = append(c.stringBindings, sb) + } - if c.Proxy != "" { - // Parse proxy URL - c.dialer, err = goexec.ParseProxyURI(c.Proxy) - if err != nil { - return err - } + if c.Proxy != "" { + // Parse proxy URL + c.dialer, err = goexec.ParseProxyURI(c.Proxy) + if err != nil { + return err + } - d := adauth.AsContextDialer(c.dialer) - c.DcerpcOptions = append(c.DcerpcOptions, dcerpc.WithDialer(d)) - c.EpmOptions = append(c.EpmOptions, dcerpc.WithDialer(d)) + d := adauth.AsContextDialer(c.dialer) + c.DcerpcOptions = append(c.DcerpcOptions, dcerpc.WithDialer(d)) + c.EpmOptions = append(c.EpmOptions, dcerpc.WithDialer(d)) - } else { - c.dialer = &net.Dialer{} - } + } else { + c.dialer = &net.Dialer{} + } - // Parse authentication parameters - if c.authOptions, err = dcerpcauth.AuthenticationOptions(ctx, c.Credential, c.Target, &dcerpcauth.Options{ - KerberosDialer: c.dialer, // Use the same net dialer as dcerpc - }); err != nil { - return fmt.Errorf("parse auth c: %w", err) - } + // Parse authentication parameters + if c.authOptions, err = dcerpcauth.AuthenticationOptions(ctx, c.Credential, c.Target, &dcerpcauth.Options{ + KerberosDialer: c.dialer, // Use the same net dialer as dcerpc + }); err != nil { + return fmt.Errorf("parse auth c: %w", err) + } - c.Host = c.Target.AddressWithoutPort() + c.Host = c.Target.AddressWithoutPort() - return + return } diff --git a/pkg/goexec/io.go b/pkg/goexec/io.go index c5a7dd8..2660065 100644 --- a/pkg/goexec/io.go +++ b/pkg/goexec/io.go @@ -1,99 +1,97 @@ 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 != "": + copy(cmd, strings.SplitN(i.Command, " ", 2)) + case i.ExecutablePath != "": + cmd[0] = i.ExecutablePath + case i.Executable != "": + cmd[0] = i.Executable + } - case i.ExecutablePath != "": - cmd[0] = i.ExecutablePath - - 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()) }