Skip to content

Commit fda6dc9

Browse files
Overhaul dialer handling
1 parent 6560051 commit fda6dc9

File tree

13 files changed

+145
-63
lines changed

13 files changed

+145
-63
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
/examples/dcerpc/dcerpc
33
/ldap
44
/examples/ldap/ldap
5-
/pkinit
65
/examples/pkinit/pkinit
6+
/smb
7+
/examples/smb/smb
78
*.exe

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,20 +64,24 @@ PKINIT implementation as well as helpers for creating and writing CCache files
6464
* UnPAC-the-Hash
6565
* Pass-the-Hash (RC4/NT or AES key)
6666
* CCache (containing TGT or ST)
67+
* SOCKS5 support
6768
* NTLM:
6869
* Pass-the-Hash
6970
* LDAP:
7071
* Kerberos, NTLM, Simple Bind
7172
* mTLS Authentication / Pass-the-Certificate (LDAPS or LDAP+StartTLS)
7273
* Channel Binding (Kerberos and NTLM)
74+
* SOCKS5 support
7375
* SMB:
7476
* Kerberos, NTLM
7577
* Signing and Sealing
78+
* SOCKS5 support
7679
* DCERPC:
7780
* Kerberos, NTLM
7881
* Raw endpoits (with port mapping)
7982
* Named pipes (SMB)
8083
* Signing and Sealing
84+
* SOCKS5 support
8185

8286
## Caveats
8387

dcerpcauth/dcerpcauth.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dcerpcauth
33
import (
44
"context"
55
"fmt"
6+
"net"
67
"strings"
78

89
"github.com/RedTeamPentesting/adauth"
@@ -25,8 +26,11 @@ type Options struct {
2526
// will be enabled for the SMB dialer, specify an empty slice to disable
2627
// this default.
2728
SMBOptions []smb2.DialerOption
28-
// PKINITOptions can be used to modify the Kerberos PKINIT behavior.
29-
PKINITOptions []pkinit.Option
29+
30+
// KerberosDialer is a custom dialer that is used to request Kerberos
31+
// tickets.
32+
KerberosDialer adauth.Dialer
33+
3034
// Debug can be set to enable debug output, for example with
3135
// adauth.NewDebugFunc(...).
3236
Debug func(string, ...any)
@@ -85,6 +89,7 @@ func AuthenticationOptions(
8589
CCachePath: creds.CCache,
8690
DisablePAFXFAST: true,
8791
DCEStyle: true,
92+
KDCDialer: upstreamOptions.KerberosDialer,
8893
}),
8994
))
9095

@@ -96,6 +101,7 @@ func AuthenticationOptions(
96101
CCachePath: creds.CCache,
97102
DisablePAFXFAST: true,
98103
DCEStyle: true,
104+
KDCDialer: upstreamOptions.KerberosDialer,
99105
}),
100106
dcerpc.WithSMBDialer(smb2.NewDialer(smbOptions...)),
101107
)
@@ -148,8 +154,13 @@ func DCERPCCredentials(ctx context.Context, creds *adauth.Credential, options *O
148154
return nil, fmt.Errorf("generate kerberos config: %w", err)
149155
}
150156

157+
dialer := options.KerberosDialer
158+
if dialer == nil {
159+
dialer = &net.Dialer{Timeout: pkinit.DefaultKerberosRoundtripDeadline}
160+
}
161+
151162
ccache, err := pkinit.Authenticate(ctx, creds.Username, strings.ToUpper(creds.Domain),
152-
creds.ClientCert, creds.ClientCertKey, krbConf, options.PKINITOptions...)
163+
creds.ClientCert, creds.ClientCertKey, krbConf, pkinit.WithDialer(adauth.AsContextDialer(dialer)))
153164
if err != nil {
154165
return nil, fmt.Errorf("PKINIT: %w", err)
155166
}

dialer.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package adauth
2+
3+
import (
4+
"context"
5+
"net"
6+
"strings"
7+
8+
"golang.org/x/net/proxy"
9+
)
10+
11+
type Dialer interface {
12+
Dial(net string, addr string) (net.Conn, error)
13+
}
14+
15+
type ContextDialer interface {
16+
DialContext(ctx context.Context, net string, addr string) (net.Conn, error)
17+
Dial(net string, addr string) (net.Conn, error)
18+
}
19+
20+
type nopContextDialer func(string, string) (net.Conn, error)
21+
22+
func (f nopContextDialer) DialContext(ctx context.Context, net string, addr string) (net.Conn, error) {
23+
return f(net, addr)
24+
}
25+
26+
func (f nopContextDialer) Dial(net string, addr string) (net.Conn, error) {
27+
return f(net, addr)
28+
}
29+
30+
// AsContextDialer converts a Dialer into a ContextDialer that either uses the
31+
// dialer's DialContext method if implemented or it uses a DialContext method
32+
// that simply calls Dial ignoring the context.
33+
func AsContextDialer(d Dialer) ContextDialer {
34+
ctxDialer, ok := d.(ContextDialer)
35+
if !ok {
36+
ctxDialer = nopContextDialer(d.Dial)
37+
}
38+
39+
return ctxDialer
40+
}
41+
42+
// SOCKS5Dialer returns a SOCKS5 dialer.
43+
func SOCKS5Dialer(network string, address string, auth *proxy.Auth, forward *net.Dialer) ContextDialer {
44+
proxyDialer, err := proxy.SOCKS5(network, address, auth, forward)
45+
if err != nil {
46+
return nopContextDialer(func(s1, s2 string) (net.Conn, error) {
47+
return nil, err
48+
})
49+
}
50+
51+
return AsContextDialer(proxyDialer)
52+
}
53+
54+
// DialerWithSOCKS5ProxyIfSet returns a SOCKS5 dialer if socks5Server is not
55+
// empty and it returns the forward dialer otherwise.
56+
func DialerWithSOCKS5ProxyIfSet(socks5Server string, forward *net.Dialer) ContextDialer {
57+
if forward == nil {
58+
forward = &net.Dialer{}
59+
}
60+
61+
if strings.TrimSpace(socks5Server) == "" {
62+
return AsContextDialer(forward)
63+
}
64+
65+
return SOCKS5Dialer("tcp", socks5Server, nil, forward)
66+
}

examples/dcerpc/main.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ import (
1818

1919
func run() error {
2020
var (
21-
debug bool
22-
authOpts = &adauth.Options{
21+
debug bool
22+
socksServer = os.Getenv("SOCKS5_SERVER")
23+
authOpts = &adauth.Options{
2324
Debug: adauth.NewDebugFunc(&debug, os.Stderr, true),
2425
}
2526
dcerpcauthOpts = &dcerpcauth.Options{
@@ -29,6 +30,7 @@ func run() error {
2930
)
3031

3132
pflag.CommandLine.BoolVar(&debug, "debug", false, "Enable debug output")
33+
pflag.CommandLine.StringVar(&socksServer, "socks", socksServer, "SOCKS5 proxy server")
3234
pflag.CommandLine.BoolVar(&namedPipe, "named-pipe", false, "Use named pipe (SMB) as transport")
3335
authOpts.RegisterFlags(pflag.CommandLine)
3436
pflag.Parse()
@@ -37,6 +39,8 @@ func run() error {
3739
return fmt.Errorf("usage: %s [options] <target>", binaryName())
3840
}
3941

42+
dcerpcauthOpts.KerberosDialer = adauth.DialerWithSOCKS5ProxyIfSet(socksServer, nil)
43+
4044
creds, target, err := authOpts.WithTarget(context.Background(), "host", pflag.Arg(0))
4145
if err != nil {
4246
return err
@@ -49,10 +53,13 @@ func run() error {
4953
return err
5054
}
5155

52-
dcerpcOpts = append(dcerpcOpts, epm.EndpointMapper(ctx,
53-
net.JoinHostPort(target.AddressWithoutPort(), "135"),
54-
dcerpc.WithInsecure(),
55-
))
56+
dcerpcOpts = append(dcerpcOpts,
57+
epm.EndpointMapper(ctx,
58+
net.JoinHostPort(target.AddressWithoutPort(), "135"),
59+
dcerpc.WithInsecure(),
60+
),
61+
dcerpc.WithDialer(adauth.DialerWithSOCKS5ProxyIfSet(socksServer, nil)),
62+
)
5663

5764
proto := "ncacn_ip_tcp:"
5865
if namedPipe {

examples/ldap/main.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ import (
1212

1313
func run() error {
1414
var (
15-
debug bool
16-
authOpts = &adauth.Options{
15+
debug bool
16+
socksServer = os.Getenv("SOCKS5_SERVER")
17+
authOpts = &adauth.Options{
1718
Debug: adauth.NewDebugFunc(&debug, os.Stderr, true),
1819
}
1920
ldapOpts = &ldapauth.Options{
@@ -22,10 +23,13 @@ func run() error {
2223
)
2324

2425
pflag.CommandLine.BoolVar(&debug, "debug", false, "Enable debug output")
26+
pflag.CommandLine.StringVar(&socksServer, "socks", socksServer, "SOCKS5 proxy server")
2527
authOpts.RegisterFlags(pflag.CommandLine)
2628
ldapOpts.RegisterFlags(pflag.CommandLine)
2729
pflag.Parse()
2830

31+
ldapOpts.SetDialer(adauth.DialerWithSOCKS5ProxyIfSet(socksServer, nil))
32+
2933
conn, err := ldapauth.Connect(context.Background(), authOpts, ldapOpts)
3034
if err != nil {
3135
return fmt.Errorf("%s connect: %w", ldapOpts.Scheme, err)

examples/pkinit/main.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"os"
77

8+
"github.com/RedTeamPentesting/adauth"
89
"github.com/RedTeamPentesting/adauth/ccachetools"
910
"github.com/RedTeamPentesting/adauth/pkinit"
1011
"github.com/spf13/pflag"
@@ -18,6 +19,7 @@ func run() error {
1819
pfxPassword string
1920
ccacheName string
2021
dc string
22+
socksServer = os.Getenv("SOCKS5_SERVER")
2123
)
2224

2325
pflag.StringVarP(&username, "username", "u", "", "Username (overrides UPN in PFX)")
@@ -26,9 +28,12 @@ func run() error {
2628
pflag.StringVarP(&pfxPassword, "pfx-password", "p", "", "PFX file password")
2729
pflag.StringVar(&ccacheName, "cache", "", "CCache output file name")
2830
pflag.StringVar(&dc, "dc", "", "Domain controller (optional)")
31+
pflag.StringVar(&socksServer, "socks", socksServer, "SOCKS5 server")
32+
2933
pflag.Parse()
3034

31-
ccache, hash, err := pkinit.UnPACTheHashFromPFX(context.Background(), username, domain, pfxFile, pfxPassword, dc)
35+
ccache, hash, err := pkinit.UnPACTheHashFromPFX(context.Background(), username, domain, pfxFile, pfxPassword, dc,
36+
pkinit.WithDialer(adauth.DialerWithSOCKS5ProxyIfSet(socksServer, nil)))
3237
if err != nil {
3338
return fmt.Errorf("UnPAC-the-Hash: %w", err)
3439
}

examples/smb/main.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package main
33
import (
44
"context"
55
"fmt"
6-
"net"
76
"os"
87
"path/filepath"
98

@@ -15,8 +14,9 @@ import (
1514

1615
func run() error {
1716
var (
18-
debug bool
19-
authOpts = &adauth.Options{
17+
debug bool
18+
socksServer = os.Getenv("SOCKS5_SERVER")
19+
authOpts = &adauth.Options{
2020
Debug: adauth.NewDebugFunc(&debug, os.Stderr, true),
2121
}
2222
smbauthOpts = &smbauth.Options{
@@ -25,6 +25,7 @@ func run() error {
2525
)
2626

2727
pflag.CommandLine.BoolVar(&debug, "debug", false, "Enable debug output")
28+
pflag.CommandLine.StringVar(&socksServer, "socks", socksServer, "SOCKS5 proxy server")
2829
authOpts.RegisterFlags(pflag.CommandLine)
2930
pflag.Parse()
3031

@@ -43,12 +44,14 @@ func run() error {
4344

4445
ctx := context.Background()
4546

47+
smbauthOpts.KerberosDialer = adauth.DialerWithSOCKS5ProxyIfSet(socksServer, nil)
48+
4649
smbDialer, err := smbauth.Dialer(ctx, creds, target, smbauthOpts)
4750
if err != nil {
4851
return fmt.Errorf("setup SMB authentication: %w", err)
4952
}
5053

51-
conn, err := net.Dial("tcp", target.Address())
54+
conn, err := adauth.DialerWithSOCKS5ProxyIfSet(socksServer, nil).DialContext(ctx, "tcp", target.Address())
5255
if err != nil {
5356
return fmt.Errorf("dial: %w", err)
5457
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ require (
1111
github.com/oiweiwei/gokrb5.fork/v9 v9.0.2
1212
github.com/spf13/pflag v1.0.6
1313
github.com/vadimi/go-ntlm v1.2.1
14+
golang.org/x/net v0.39.0
1415
software.sslmate.com/src/go-pkcs12 v0.5.0
1516
)
1617

@@ -29,7 +30,6 @@ require (
2930
github.com/mattn/go-isatty v0.0.20 // indirect
3031
github.com/rs/zerolog v1.34.0 // indirect
3132
golang.org/x/crypto v0.37.0 // indirect
32-
golang.org/x/net v0.39.0 // indirect
3333
golang.org/x/sys v0.32.0 // indirect
3434
golang.org/x/text v0.24.0 // indirect
3535
)

ldapauth/gssapi.go

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import (
99
"encoding/hex"
1010
"errors"
1111
"fmt"
12-
"net"
1312
"strings"
1413

14+
"github.com/RedTeamPentesting/adauth"
1515
"github.com/RedTeamPentesting/adauth/compat"
1616
"github.com/RedTeamPentesting/adauth/pkinit"
1717
"github.com/jcmturner/gokrb5/v8/config"
@@ -45,7 +45,7 @@ type gssapiClient struct {
4545
}
4646

4747
func newClientFromCCache(
48-
username string, domain string, ccachePath string, krb5Conf *config.Config, dialer Dialer,
48+
username string, domain string, ccachePath string, krb5Conf *config.Config, dialer adauth.Dialer,
4949
) (*gssapiClient, error) {
5050
ccache, err := credentials.LoadCCache(ccachePath)
5151
if err != nil {
@@ -76,9 +76,9 @@ func newClientFromCCache(
7676

7777
func newPKINITClient(
7878
ctx context.Context, username string, domain string, cert *x509.Certificate, key *rsa.PrivateKey,
79-
krb5Conf *config.Config, dialer Dialer,
79+
krb5Conf *config.Config, dialer adauth.Dialer,
8080
) (*gssapiClient, error) {
81-
ctxDialer := ContextDialer(dialer)
81+
ctxDialer := adauth.AsContextDialer(dialer)
8282

8383
ccache, err := pkinit.Authenticate(ctx, username, domain, cert, key, krb5Conf, pkinit.WithDialer(ctxDialer))
8484
if err != nil {
@@ -374,18 +374,3 @@ func krb5TokenAuthenticator(
374374

375375
return auth, nil
376376
}
377-
378-
type nopContextDialer func(string, string) (net.Conn, error)
379-
380-
func (f nopContextDialer) DialContext(ctx context.Context, net string, addr string) (net.Conn, error) {
381-
return f(net, addr)
382-
}
383-
384-
func ContextDialer(d Dialer) pkinit.ContextDialer {
385-
ctxDialer, ok := d.(pkinit.ContextDialer)
386-
if !ok {
387-
ctxDialer = nopContextDialer(d.Dial)
388-
}
389-
390-
return ctxDialer
391-
}

0 commit comments

Comments
 (0)