Files
2025-07-09 10:26:18 +02:00

104 lines
2.8 KiB
Go

package smbauth
import (
"context"
"fmt"
"github.com/RedTeamPentesting/adauth"
"github.com/RedTeamPentesting/adauth/dcerpcauth"
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"
"github.com/oiweiwei/go-smb2.fork"
)
// 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,
// encryption/sealing will be enabled. Specify an empty slice to disable
// this default.
SMBOptions []msrpcSMB2.DialerOption
// KerberosDialer is a custom dialer that is used to request Kerberos
// tickets.
KerberosDialer adauth.Dialer
// 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
}
opts.Debug(format, a...)
}
// 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{
Debug: options.debug,
KerberosDialer: options.KerberosDialer,
})
if err != nil {
return nil, err
}
dialerOptions := options.SMBOptions
if dialerOptions == nil {
dialerOptions = append(dialerOptions, msrpcSMB2.WithSeal())
}
switch {
case target.UseKerberos || creds.ClientCert != nil:
spn, err := target.SPN(ctx)
if err != nil {
return nil, fmt.Errorf("build SPN: %w", err)
}
options.debug("Using Kerberos with SPN %q", spn)
krbConf, err := creds.KerberosConfig(ctx)
if err != nil {
return nil, fmt.Errorf("generate Kerberos config: %w", err)
}
dialerOptions = append(dialerOptions, msrpcSMB2.WithSecurity(
gssapi.WithTargetName(spn),
gssapi.WithCredential(smbCreds),
gssapi.WithMechanismFactory(ssp.KRB5, &krb5.Config{
KRB5Config: krbConf,
CCachePath: creds.CCache,
DisablePAFXFAST: true,
KDCDialer: options.KerberosDialer,
AnyServiceClassSPN: true,
}),
))
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
}
}