diff --git a/README.md b/README.md index ecdf40a..d5d253f 100644 --- a/README.md +++ b/README.md @@ -53,9 +53,9 @@ Kerberos authentication is possible even when specifying the target via IP address if reverse lookups are possible. Similarly, the domain can be omitted when the target hostname contains the domain. -The library also contains helper packages for LDAP and DCERPC, a Kerebros PKINIT -implementation as well as helpers for creating and writing CCache files (see -examples). +The library also contains helper packages for LDAP, SMB and DCERPC, a Kerebros +PKINIT implementation as well as helpers for creating and writing CCache files +(see examples). ## Features @@ -70,12 +70,14 @@ examples). * Kerberos, NTLM, Simple Bind * mTLS Authentication / Pass-the-Certificate (LDAPS or LDAP+StartTLS) * Channel Binding (Kerberos and NTLM) +* SMB + * Kerberos, NTLM + * Signing and Sealing * DCERPC: * Kerberos, NTLM * Raw endpoits (with port mapping) * Named pipes (SMB) - * Signing - * Sealing + * Signing and Sealing ## Caveats diff --git a/dcerpcauth/dcerpcauth.go b/dcerpcauth/dcerpcauth.go index 602ecd6..49d216f 100644 --- a/dcerpcauth/dcerpcauth.go +++ b/dcerpcauth/dcerpcauth.go @@ -21,9 +21,9 @@ import ( // function. type Options struct { // SMBOptions holds options for the SMB dialer. This dialer is only used - // with the named pipe transport. If SMBOptions is nil, sealing will be - // enabled for the smb dialer, specify an empty slice to disable this - // default. + // with the named pipe transport. If SMBOptions is nil, encryption/sealing + // will be enabled for the SMB dialer, specify an empty slice to disable + // this default. SMBOptions []smb2.DialerOption // PKINITOptions can be used to modify the Kerberos PKINIT behavior. PKINITOptions []pkinit.Option diff --git a/examples/dcerpc/main.go b/examples/dcerpc/main.go index 8be9282..6b64add 100644 --- a/examples/dcerpc/main.go +++ b/examples/dcerpc/main.go @@ -13,7 +13,6 @@ import ( "github.com/oiweiwei/go-msrpc/msrpc/dtyp" "github.com/oiweiwei/go-msrpc/msrpc/epm/epm/v3" "github.com/oiweiwei/go-msrpc/msrpc/samr/samr/v1" - "github.com/oiweiwei/go-msrpc/ssp/gssapi" "github.com/spf13/pflag" ) @@ -43,7 +42,7 @@ func run() error { return err } - ctx := gssapi.NewSecurityContext(context.Background()) + ctx := context.Background() dcerpcOpts, err := dcerpcauth.AuthenticationOptions(ctx, creds, target, dcerpcauthOpts) if err != nil { diff --git a/examples/smb/main.go b/examples/smb/main.go index fd5958a..099c2f4 100644 --- a/examples/smb/main.go +++ b/examples/smb/main.go @@ -1,97 +1,105 @@ package main import ( - "context" - "fmt" - "github.com/RedTeamPentesting/adauth/smbauth" - "github.com/oiweiwei/go-msrpc/smb2" - "github.com/oiweiwei/go-msrpc/ssp" - "net" - "os" - "path/filepath" + "context" + "fmt" + "net" + "os" + "path/filepath" - "github.com/RedTeamPentesting/adauth" - "github.com/oiweiwei/go-msrpc/ssp/gssapi" - "github.com/spf13/pflag" + "github.com/RedTeamPentesting/adauth/smbauth" + + "github.com/RedTeamPentesting/adauth" + "github.com/spf13/pflag" ) -var ( - debug bool - authOpts = &adauth.Options{ - Debug: adauth.NewDebugFunc(&debug, os.Stderr, true), - } -) - -func init() { - pflag.CommandLine.BoolVar(&debug, "debug", false, "Enable debug output") - authOpts.RegisterFlags(pflag.CommandLine) - gssapi.AddMechanism(ssp.SPNEGO) - gssapi.AddMechanism(ssp.NTLM) -} - func run() error { - pflag.Parse() + var ( + debug bool + authOpts = &adauth.Options{ + Debug: adauth.NewDebugFunc(&debug, os.Stderr, true), + } + smbauthOpts = &smbauth.Options{ + Debug: authOpts.Debug, + } + ) - if len(pflag.Args()) != 1 { - return fmt.Errorf("usage: %s [--debug]", binaryName()) - } + pflag.CommandLine.BoolVar(&debug, "debug", false, "Enable debug output") + authOpts.RegisterFlags(pflag.CommandLine) + pflag.Parse() - creds, target, err := authOpts.WithTarget(context.Background(), "host", pflag.Arg(0)) - if err != nil { - return err - } + if len(pflag.Args()) != 1 { + return fmt.Errorf("usage: %s [options] ", binaryName()) + } - ctx := gssapi.NewSecurityContext(context.Background()) + creds, target, err := authOpts.WithTarget(context.Background(), "host", pflag.Arg(0)) + if err != nil { + return err + } - smbOpts, secOpts, err := smbauth.AuthenticationOptions(ctx, creds, target, &smbauth.Options{}) - if err != nil { - return err - } + if target.Port == "" { + target.Port = "445" + } - // Create go-smb2 Dialer - dialer := smb2.NewDialer(append(smbOpts, smb2.WithSecurity(secOpts...))...) + ctx := context.Background() - conn, err := net.Dial("tcp", net.JoinHostPort(target.AddressWithoutPort(), "445")) - if err != nil { - return err - } - defer conn.Close() + smbDialer, err := smbauth.Dialer(ctx, creds, target, smbauthOpts) + if err != nil { + return fmt.Errorf("setup SMB authentication: %w", err) + } - sess, err := dialer.Dial(conn) - if err != nil { - return err - } - defer sess.Logoff() + conn, err := net.Dial("tcp", target.Address()) + if err != nil { + return fmt.Errorf("dial: %w", err) + } - names, err := sess.ListSharenames() - if err != nil { - return err - } + defer conn.Close() - for _, name := range names { - fmt.Println(name) - } - return nil + sess, err := smbDialer.DialContext(ctx, conn) + if err != nil { + return fmt.Errorf("create session: %w", err) + } + + defer sess.Logoff() + + shares, err := sess.ListSharenames() + if err != nil { + return fmt.Errorf("list share names: %w", err) + } + + if len(shares) == 0 { + fmt.Println("No shares available") + + return nil + } + + fmt.Println("Shares:") + + for _, share := range shares { + fmt.Printf(" - %s\n", share) + } + + return nil } func binaryName() string { - executable, err := os.Executable() - if err == nil { - return filepath.Base(executable) - } + executable, err := os.Executable() + if err == nil { + return filepath.Base(executable) + } - if len(os.Args) > 0 { - return filepath.Base(os.Args[0]) - } + if len(os.Args) > 0 { + return filepath.Base(os.Args[0]) + } - return "list-shares" + return "smb" } func main() { - err := run() - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) + err := run() + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) - os.Exit(1) - } + os.Exit(1) + } } diff --git a/go.mod b/go.mod index 48b7f54..d830bb0 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/go-ldap/ldap/v3 v3.4.11-0.20250110131057-5d1b644709df github.com/jcmturner/gokrb5/v8 v8.4.4 github.com/oiweiwei/go-msrpc v1.2.5 + github.com/oiweiwei/go-smb2.fork v1.0.0 github.com/oiweiwei/gokrb5.fork/v9 v9.0.2 github.com/spf13/pflag v1.0.6 github.com/vadimi/go-ntlm v1.2.1 @@ -26,10 +27,9 @@ require ( github.com/jcmturner/rpc/v2 v2.0.3 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/oiweiwei/go-smb2.fork v1.0.0 // indirect - github.com/rs/zerolog v1.33.0 // indirect - golang.org/x/crypto v0.35.0 // indirect - golang.org/x/net v0.36.0 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/text v0.22.0 // indirect + github.com/rs/zerolog v1.34.0 // indirect + golang.org/x/crypto v0.37.0 // indirect + golang.org/x/net v0.39.0 // indirect + golang.org/x/sys v0.32.0 // indirect + golang.org/x/text v0.24.0 // indirect ) diff --git a/go.sum b/go.sum index 4e4c48f..7496471 100644 --- a/go.sum +++ b/go.sum @@ -55,10 +55,7 @@ github.com/oiweiwei/gokrb5.fork/v9 v9.0.2/go.mod h1:KEnkAYUYqZ5VwzxLFbv3JHlRhCvd github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= -github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= @@ -82,8 +79,6 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= -golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -102,8 +97,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= -golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= +golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= +golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -124,8 +119,6 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= @@ -146,8 +139,6 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/smbauth/smbauth.go b/smbauth/smbauth.go index 2415a7a..3284479 100644 --- a/smbauth/smbauth.go +++ b/smbauth/smbauth.go @@ -1,167 +1,103 @@ package smbauth import ( - "context" - "fmt" - "strings" + "context" + "fmt" - "github.com/RedTeamPentesting/adauth" - "github.com/RedTeamPentesting/adauth/pkinit" + "github.com/RedTeamPentesting/adauth" + "github.com/RedTeamPentesting/adauth/dcerpcauth" + "github.com/RedTeamPentesting/adauth/pkinit" - "github.com/jcmturner/gokrb5/v8/keytab" - "github.com/oiweiwei/go-msrpc/smb2" - "github.com/oiweiwei/go-msrpc/ssp" - "github.com/oiweiwei/go-msrpc/ssp/credential" - "github.com/oiweiwei/go-msrpc/ssp/gssapi" - "github.com/oiweiwei/go-msrpc/ssp/krb5" + "github.com/oiweiwei/go-smb2.fork" + + msrpcSMB2 "github.com/oiweiwei/go-msrpc/smb2" + "github.com/oiweiwei/go-msrpc/ssp" + "github.com/oiweiwei/go-msrpc/ssp/gssapi" + "github.com/oiweiwei/go-msrpc/ssp/krb5" ) -// Options holds options that modify the behavior of the AuthenticationOptions -// function. +// Options holds options that modify the behavior of the Dialer function. type Options struct { - // SMBOptions holds options for the SMB dialer. If SMBOptions is nil, - // sealing will be enabled for the smb dialer, specify an empty slice - // to disable this default. - SMBOptions []smb2.DialerOption + // SMBOptions holds options for the SMB dialer. If SMBOptions is nil, + // encryption/sealing will be enabled. Specify an empty slice to disable + // this default. + SMBOptions []msrpcSMB2.DialerOption - // PKINITOptions can be used to modify the Kerberos PKINIT behavior. - PKINITOptions []pkinit.Option + // PKINITOptions can be used to modify the Kerberos PKINIT behavior. + PKINITOptions []pkinit.Option - // Debug can be set to enable debug output, for example with - // adauth.NewDebugFunc(...). - Debug func(string, ...any) + // Debug can be set to enable debug output, for example with + // adauth.NewDebugFunc(...). + Debug func(string, ...any) } func (opts *Options) debug(format string, a ...any) { - if opts == nil || opts.Debug == nil { - return - } + if opts == nil || opts.Debug == nil { + return + } - opts.Debug(format, a...) + opts.Debug(format, a...) } -// AuthenticationOptions returns the security context options for -// smb2.WithSecurity. It is possible to configure the SMB, PKINIT -// and debug behavior using the optional upstreamOptions argument. -func AuthenticationOptions( - ctx context.Context, creds *adauth.Credential, target *adauth.Target, - upstreamOptions *Options, -) (dialOptions []smb2.DialerOption, secOptions []gssapi.ContextOption, err error) { +// Dialer returns an SMB dialer which is prepared for authentication with the +// given credentials. The dialer can be further customized with +// options.SMBDialerOptions. +func Dialer( + ctx context.Context, creds *adauth.Credential, target *adauth.Target, options *Options, +) (*smb2.Dialer, error) { + smbCreds, err := dcerpcauth.DCERPCCredentials(ctx, creds, &dcerpcauth.Options{ + PKINITOptions: options.PKINITOptions, + Debug: options.debug, + }) + if err != nil { + return nil, err + } - if dialOptions = upstreamOptions.SMBOptions; dialOptions == nil { - dialOptions = []smb2.DialerOption{smb2.WithSeal()} - } + dialerOptions := options.SMBOptions + if dialerOptions == nil { + dialerOptions = append(dialerOptions, msrpcSMB2.WithSeal()) + } - smbCredentials, err := SMBCredentials(ctx, creds, upstreamOptions) - if err != nil { - return nil, nil, err - } + switch { + case target.UseKerberos || creds.ClientCert != nil: + spn, err := target.SPN(ctx) + if err != nil { + return nil, fmt.Errorf("build SPN: %w", err) + } - switch { - case target.UseKerberos || creds.ClientCert != nil: - spn, err := target.SPN(ctx) - if err != nil { - return nil, nil, fmt.Errorf("build SPN: %w", err) - } - upstreamOptions.debug("Using Kerberos with SPN %q", spn) + options.debug("Using Kerberos with SPN %q", spn) - krbConf, err := creds.KerberosConfig(ctx) - if err != nil { - return nil, nil, fmt.Errorf("generate Kerberos config: %w", err) - } + krbConf, err := creds.KerberosConfig(ctx) + if err != nil { + return nil, fmt.Errorf("generate Kerberos config: %w", err) + } - secOptions = append(secOptions, - gssapi.WithTargetName(spn), - gssapi.WithCredential(smbCredentials), - gssapi.WithMechanismFactory(ssp.KRB5, &krb5.Config{ - KRB5Config: krbConf, - CCachePath: creds.CCache, - DisablePAFXFAST: true, - DCEStyle: true, - }), - ) - default: - upstreamOptions.debug("Using NTLM") + dialerOptions = append(dialerOptions, msrpcSMB2.WithSecurity( + gssapi.WithTargetName(spn), + gssapi.WithCredential(smbCreds), + gssapi.WithMechanismFactory(ssp.KRB5, &krb5.Config{ + KRB5Config: krbConf, + CCachePath: creds.CCache, + DisablePAFXFAST: true, + }), + )) - secOptions = append(secOptions, - gssapi.WithCredential(smbCredentials), - gssapi.WithMechanismFactory(ssp.NTLM), - ) - // Try fetching SPN - if spn, err := target.SPN(ctx); err == nil { - secOptions = append(secOptions, gssapi.WithTargetName(spn)) - } - } - return -} - -func SMBCredentials(ctx context.Context, creds *adauth.Credential, options *Options) (credential.Credential, error) { - switch { - case creds.Password != "": - options.debug("Authenticating with password") - - return credential.NewFromPassword(creds.LogonNameWithUpperCaseDomain(), creds.Password), nil - case creds.AESKey != "": - options.debug("Authenticating with AES key") - - keyTab, err := creds.Keytab() - if err != nil { - return nil, fmt.Errorf("create keytab: %w", err) - } - - return &keytabCredentials{username: creds.Username, domain: creds.Domain, keytab: keyTab}, nil - case creds.NTHash != "": - options.debug("Authenticating with NT hash") - - return credential.NewFromNTHash(creds.LogonNameWithUpperCaseDomain(), creds.NTHash), nil - case creds.PasswordIsEmtpyString: - options.debug("Authenticating with empty password") - - return credential.NewFromPassword(strings.ToUpper(creds.Domain)+`\`+creds.Username, ""), nil - case creds.ClientCert != nil: - options.debug("Authenticating with client certificate (PKINIT)") - - krbConf, err := creds.KerberosConfig(ctx) - if err != nil { - return nil, fmt.Errorf("generate kerberos config: %w", err) - } - - ccache, err := pkinit.Authenticate(ctx, creds.Username, strings.ToUpper(creds.Domain), - creds.ClientCert, creds.ClientCertKey, krbConf, options.PKINITOptions...) - if err != nil { - return nil, fmt.Errorf("PKINIT: %w", err) - } - - return credential.NewFromCCache(creds.LogonNameWithUpperCaseDomain(), ccache), nil - case creds.CCache != "": - options.debug("Authenticating with ccache") - - return credential.NewFromPassword(creds.LogonNameWithUpperCaseDomain(), ""), nil - default: - return nil, fmt.Errorf("no credentials available") - } -} - -type keytabCredentials struct { - keytab *keytab.Keytab - username string - domain string -} - -var _ credential.KeytabV8 = &keytabCredentials{} - -func (ktc *keytabCredentials) DomainName() string { - return strings.ToUpper(ktc.domain) -} - -func (ktc *keytabCredentials) Workstation() string { - return "" -} - -func (ktc *keytabCredentials) UserName() string { - return ktc.username -} - -func (ktc *keytabCredentials) Keytab() *keytab.Keytab { - return ktc.keytab + return msrpcSMB2.NewDialer(dialerOptions...), nil + default: + options.debug("Using NTLM") + + secOptions := []gssapi.ContextOption{ + gssapi.WithCredential(smbCreds), + gssapi.WithMechanismFactory(ssp.NTLM), + } + + spn, err := target.SPN(ctx) + if err == nil { + secOptions = append(secOptions, gssapi.WithTargetName(spn)) + } + + dialerOptions = append(dialerOptions, msrpcSMB2.WithSecurity(secOptions...)) + + return msrpcSMB2.NewDialer(dialerOptions...), nil + } }