diff options
-rw-r--r-- | TODO.md | 2 | ||||
-rw-r--r-- | cmd/wmi.go | 11 | ||||
-rw-r--r-- | internal/client/dce/dce.go | 1 | ||||
-rw-r--r-- | internal/exec/wmi/exec.go | 125 |
4 files changed, 17 insertions, 122 deletions
@@ -23,7 +23,7 @@ ### WMI - [X] Add WMI module -- [ ] Clean up WMI module +- [X] Clean up WMI module - [ ] WMI `reg` subcommand - read & edit the registry - [ ] File transfer functionality @@ -17,8 +17,9 @@ func wmiCmdInit() { } func wmiCallCmdInit() { - wmiCallCmd.Flags().StringVarP(&wmi.Method, "method", "m", "", `WMI Method to call (i.e. "Create")`) + wmiCallCmd.Flags().StringVarP(&dceConfig.Resource, "namespace", "n", "//./root/cimv2", "WMI namespace") wmiCallCmd.Flags().StringVarP(&wmi.Class, "class", "C", "", `WMI class to instantiate (i.e. "Win32_Process")`) + wmiCallCmd.Flags().StringVarP(&wmi.Method, "method", "m", "", `WMI Method to call (i.e. "Create")`) wmiCallCmd.Flags().StringVarP(&wmi.Args, "args", "A", "{}", `WMI Method argument(s) in JSON dictionary format (i.e. {"CommandLine":"calc.exe"})`) if err := wmiCallCmd.MarkFlagRequired("method"); err != nil { panic(err) @@ -35,10 +36,9 @@ func wmiProcessCmdInit() { var ( wmi struct { - Namespace string // TODO - Class string - Method string - Args string + Class string + Method string + Args string } wmiMethodArgsMap map[string]any @@ -64,7 +64,6 @@ References: return }), Run: func(cmd *cobra.Command, args []string) { - executor := wmiexec.Module{} cleanCfg := &exec.CleanupConfig{} // TODO connCfg := &exec.ConnectionConfig{ diff --git a/internal/client/dce/dce.go b/internal/client/dce/dce.go index 142eda9..0230c2a 100644 --- a/internal/client/dce/dce.go +++ b/internal/client/dce/dce.go @@ -21,6 +21,7 @@ type ConnectionMethodDCEConfig struct { Options []dcerpc.Option // Options stores the options that will be passed to all dialers DceOptions []dcerpc.Option // DceOptions are the options passed to dcerpc.Dial EpmOptions []dcerpc.Option // EpmOptions are the options passed to epm.EndpointMapper + Resource string // Resource stores the target network resource (usually for DCOM) } func (cfg *ConnectionMethodDCEConfig) GetDce(ctx context.Context, cred *adauth.Credential, target *adauth.Target, endpoint, object string, arbOpts ...dcerpc.Option) (cc dcerpc.Conn, err error) { diff --git a/internal/exec/wmi/exec.go b/internal/exec/wmi/exec.go index b61bf5c..7ae33ba 100644 --- a/internal/exec/wmi/exec.go +++ b/internal/exec/wmi/exec.go @@ -8,14 +8,12 @@ import ( "github.com/FalconOpsLLC/goexec/internal/client/dce" "github.com/FalconOpsLLC/goexec/internal/exec" "github.com/RedTeamPentesting/adauth" - "github.com/RedTeamPentesting/adauth/dcerpcauth" "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/ssp/gssapi" "github.com/rs/zerolog" ) @@ -72,7 +70,7 @@ func (mod *Module) Connect(ctx context.Context, creds *adauth.Credential, target ORPCThis: ORPCThis, ClassID: wmi.Level1LoginClassID.GUID(), IIDs: []*dcom.IID{iwbemlevel1login.Level1LoginIID}, - RequestedProtocolSequences: []uint16{ProtocolSequenceRPC, ProtocolSequenceNP}, // TODO: dynamic + RequestedProtocolSequences: []uint16{ProtocolSequenceRPC}, // TODO: Named pipe support }) if err != nil { return fmt.Errorf("request remote activation: %w", err) @@ -85,8 +83,13 @@ func (mod *Module) Connect(ctx context.Context, creds *adauth.Credential, target return errors.New("remote activation failed") } ipid := act.InterfaceData[0].GetStandardObjectReference().Std.IPID - for _, sb := range retBinds { - //sb.NetworkAddr = target.AddressWithoutPort() // TODO: check if sb.NetworkAddr contains the port + + for _, b := range retBinds { + sb, err := dcerpc.ParseStringBinding("ncacn_ip_tcp:" + b.NetworkAddr) + if err != nil { + log.Debug().Err(err).Msg("Failed to parse string binding") + } + sb.NetworkAddress = target.AddressWithoutPort() dceOpts = append(dceOpts, dcerpc.WithEndpoint(sb.String())) } @@ -99,7 +102,7 @@ func (mod *Module) Connect(ctx context.Context, creds *adauth.Credential, target } login, err := loginClient.NTLMLogin(ctx, &iwbemlevel1login.NTLMLoginRequest{ This: ORPCThis, - NetworkResource: "//./root/cimv2", // TODO: make this dynamic + NetworkResource: cfg.Resource, }) if err != nil { return fmt.Errorf("ntlm login: %w", err) @@ -107,120 +110,12 @@ func (mod *Module) Connect(ctx context.Context, creds *adauth.Credential, target mod.sc, err = iwbemservices.NewServicesClient(ctx, mod.dce, dcom.WithIPID(login.Namespace.InterfacePointer().IPID())) if err != nil { - return fmt.Errorf("iwbemservices.NewServicesClient: %w", err) + return fmt.Errorf("create services client: %w", err) } } - return } -func (mod *Module) _Connect(ctx context.Context, creds *adauth.Credential, target *adauth.Target, _ *exec.ConnectionConfig) (err error) { - - var baseOpts, authOpts []dcerpc.Option - var ipid *dcom.IPID // This will store the IPID of the remote instance - var bind2Opts []dcerpc.Option - - ctx = gssapi.NewSecurityContext(ctx) - log := zerolog.Ctx(ctx).With(). - Str("module", "tsch"). - Str("func", "Connect").Logger() - - // Assemble DCERPC options - { - baseOpts = []dcerpc.Option{ - dcerpc.WithLogger(log), - dcerpc.WithSign(), // Enforce signing - dcerpc.WithSeal(), // Enforce packet stub encryption - } - // Add target name option if possible - if tn, err := target.Hostname(ctx); err == nil { - baseOpts = append(baseOpts, dcerpc.WithTargetName(tn)) - } else { - log.Debug().Err(err).Msg("Failed to get target hostname") - } - // Parse target and credentials - if authOpts, err = dcerpcauth.AuthenticationOptions(ctx, creds, target, &dcerpcauth.Options{}); err != nil { - return fmt.Errorf("parse authentication options: %w", err) - } - } - - // Establish first connection (REMACT) - { - // Connection options - rp := "ncacn_ip_tcp" // Underlying protocol - ro := 135 // RPC port - rb := fmt.Sprintf("%s:[%d]", rp, ro) // RPC binding - - // Create DCERPC dialer - mod.dce, err = dcerpc.Dial(ctx, target.AddressWithoutPort(), append(baseOpts, append(authOpts, dcerpc.WithEndpoint(rb))...)...) - if err != nil { - return fmt.Errorf("create DCERPC dialer: %w", err) - } - // Create remote activation client - ia, err := iactivation.NewActivationClient(ctx, mod.dce, append(baseOpts, dcerpc.WithEndpoint(rb))...) - if err != nil { - return fmt.Errorf("create activation client: %w", err) - } - // Send remote activation request - act, err := ia.RemoteActivation(ctx, &iactivation.RemoteActivationRequest{ - ORPCThis: ORPCThis, - ClassID: wmi.Level1LoginClassID.GUID(), - IIDs: []*dcom.IID{iwbemlevel1login.Level1LoginIID}, - RequestedProtocolSequences: []uint16{ProtocolSequenceRPC, ProtocolSequenceNP}, // TODO: dynamic - }) - if err != nil { - return fmt.Errorf("request remote activation: %w", err) - } - if act.HResult != 0 { - return fmt.Errorf("remote activation failed with code %d", act.HResult) - } - retBinds := act.OXIDBindings.GetStringBindings() - if len(act.InterfaceData) < 1 || len(retBinds) < 1 { - return errors.New("remote activation failed") - } - ipid = act.InterfaceData[0].GetStandardObjectReference().Std.IPID - - // This ensures that the original target address/hostname is used in the string binding - origBind := retBinds[0].String() - if bind, err := dcerpc.ParseStringBinding(origBind); err != nil { - log.Warn().Str("binding", origBind).Err(err).Msg("Failed to parse binding string returned by server") - bind2Opts = act.OXIDBindings.EndpointsByProtocol(rp) // Try using the server supplied string binding - } else { - bind.NetworkAddress = target.AddressWithoutPort() // Replace address/hostname in new string binding - bs := bind.String() - log.Info().Str("binding", bs).Msg("found binding") - bind2Opts = append(bind2Opts, dcerpc.WithEndpoint(bs)) // Use the new string binding - } - } - - // Establish second connection (WMI) - { - bind2Opts = append(bind2Opts, authOpts...) - mod.dce, err = dcerpc.Dial(ctx, target.AddressWithoutPort(), append(baseOpts, bind2Opts...)...) - if err != nil { - return fmt.Errorf("dial WMI: %w", err) - } - // Create login client - loginClient, err := iwbemlevel1login.NewLevel1LoginClient(ctx, mod.dce, append(baseOpts, dcom.WithIPID(ipid))...) - if err != nil { - return fmt.Errorf("initialize wbem login client: %w", err) - } - login, err := loginClient.NTLMLogin(ctx, &iwbemlevel1login.NTLMLoginRequest{ // TODO: Other login opts/methods? - This: ORPCThis, - NetworkResource: "//./root/cimv2", // TODO: make this dynamic - }) - if err != nil { - return fmt.Errorf("ntlm login: %w", err) - } - - mod.sc, err = iwbemservices.NewServicesClient(ctx, mod.dce, dcom.WithIPID(login.Namespace.InterfacePointer().IPID())) - if err != nil { - return fmt.Errorf("iwbemservices.NewServicesClient: %w", err) - } - } - return nil -} - func (mod *Module) Exec(ctx context.Context, ecfg *exec.ExecutionConfig) (err error) { log := zerolog.Ctx(ctx).With(). Str("module", "tsch"). |