mirror of
https://github.com/RedTeamPentesting/adauth
synced 2026-06-08 12:20:46 +00:00
Add barebones smbauth module w/ basic example
This commit is contained in:
committed by
Erik Geiser
parent
7b22106170
commit
09ec08f3d7
@@ -0,0 +1,97 @@
|
||||
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"
|
||||
|
||||
"github.com/RedTeamPentesting/adauth"
|
||||
"github.com/oiweiwei/go-msrpc/ssp/gssapi"
|
||||
"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()
|
||||
|
||||
if len(pflag.Args()) != 1 {
|
||||
return fmt.Errorf("usage: %s <target> [--debug]", binaryName())
|
||||
}
|
||||
|
||||
creds, target, err := authOpts.WithTarget(context.Background(), "host", pflag.Arg(0))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := gssapi.NewSecurityContext(context.Background())
|
||||
|
||||
smbOpts, secOpts, err := smbauth.AuthenticationOptions(ctx, creds, target, &smbauth.Options{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create go-smb2 Dialer
|
||||
dialer := smb2.NewDialer(append(smbOpts, smb2.WithSecurity(secOpts...))...)
|
||||
|
||||
conn, err := net.Dial("tcp", net.JoinHostPort(target.AddressWithoutPort(), "445"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
sess, err := dialer.Dial(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sess.Logoff()
|
||||
|
||||
names, err := sess.ListSharenames()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
fmt.Println(name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func binaryName() string {
|
||||
executable, err := os.Executable()
|
||||
if err == nil {
|
||||
return filepath.Base(executable)
|
||||
}
|
||||
|
||||
if len(os.Args) > 0 {
|
||||
return filepath.Base(os.Args[0])
|
||||
}
|
||||
|
||||
return "list-shares"
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := run()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package smbauth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/RedTeamPentesting/adauth"
|
||||
"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"
|
||||
)
|
||||
|
||||
// Options holds options that modify the behavior of the AuthenticationOptions
|
||||
// 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
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
func (opts *Options) debug(format string, a ...any) {
|
||||
if opts == nil || opts.Debug == nil {
|
||||
return
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
if dialOptions = upstreamOptions.SMBOptions; dialOptions == nil {
|
||||
dialOptions = []smb2.DialerOption{smb2.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, nil, fmt.Errorf("build SPN: %w", err)
|
||||
}
|
||||
upstreamOptions.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)
|
||||
}
|
||||
|
||||
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")
|
||||
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user