Skip to content

Commit 286b3cf

Browse files
ropnopRonnie Flathers
and
Ronnie Flathers
authored
Add ability to bind with only NTLM Hash instead of password (#269)
* add NTLMBindWithHash, update go.mod * add NTLMBindWithHash, update go.mod on v2 Co-authored-by: Ronnie Flathers <[email protected]>
1 parent c2c0783 commit 286b3cf

File tree

6 files changed

+76
-8
lines changed

6 files changed

+76
-8
lines changed

bind.go

+24-3
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,8 @@ type NTLMBindRequest struct {
400400
Username string
401401
// Password is the credentials to bind with
402402
Password string
403+
// Hash is the hex NTLM hash to bind with. Password or hash must be provided
404+
Hash string
403405
// Controls are optional controls to send with the bind request
404406
Controls []Control
405407
}
@@ -441,9 +443,20 @@ func (l *Conn) NTLMBind(domain, username, password string) error {
441443
return err
442444
}
443445

446+
// NTLMBindWithHash performs an NTLM Bind with an NTLM hash instead of plaintext password (pass-the-hash)
447+
func (l *Conn) NTLMBindWithHash(domain, username, hash string) error {
448+
req := &NTLMBindRequest{
449+
Domain: domain,
450+
Username: username,
451+
Hash: hash,
452+
}
453+
_, err := l.NTLMChallengeBind(req)
454+
return err
455+
}
456+
444457
// NTLMChallengeBind performs the NTLMSSP bind operation defined in the given request
445458
func (l *Conn) NTLMChallengeBind(ntlmBindRequest *NTLMBindRequest) (*NTLMBindResult, error) {
446-
if ntlmBindRequest.Password == "" {
459+
if ntlmBindRequest.Password == "" && ntlmBindRequest.Hash == "" {
447460
return nil, NewError(ErrorEmptyPassword, errors.New("ldap: empty password not allowed by the client"))
448461
}
449462

@@ -481,8 +494,16 @@ func (l *Conn) NTLMChallengeBind(ntlmBindRequest *NTLMBindRequest) (*NTLMBindRes
481494
}
482495
}
483496
if ntlmsspChallenge != nil {
484-
// generate a response message to the challenge with the given Username/Password
485-
responseMessage, err := ntlmssp.ProcessChallenge(ntlmsspChallenge, ntlmBindRequest.Username, ntlmBindRequest.Password)
497+
var err error
498+
var responseMessage []byte
499+
// generate a response message to the challenge with the given Username/Password if password is provided
500+
if ntlmBindRequest.Password != "" {
501+
responseMessage, err = ntlmssp.ProcessChallenge(ntlmsspChallenge, ntlmBindRequest.Username, ntlmBindRequest.Password)
502+
} else if ntlmBindRequest.Hash != "" {
503+
responseMessage, err = ntlmssp.ProcessChallengeWithHash(ntlmsspChallenge, ntlmBindRequest.Username, ntlmBindRequest.Hash)
504+
} else {
505+
err = fmt.Errorf("need a password or hash to generate reply")
506+
}
486507
if err != nil {
487508
return result, fmt.Errorf("parsing ntlm-challenge: %s", err)
488509
}

go.mod

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,8 @@ module github.com/go-ldap/ldap
22

33
go 1.13
44

5-
require github.com/go-asn1-ber/asn1-ber v1.5.0
5+
require (
6+
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c
7+
github.com/go-asn1-ber/asn1-ber v1.5.0
8+
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 // indirect
9+
)

go.sum

+9
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,11 @@
1+
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
2+
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
13
github.com/go-asn1-ber/asn1-ber v1.5.0 h1:/S4hO/AO6tLMlPX0oftGSOcdGJJN/MuYzfgWRMn199E=
24
github.com/go-asn1-ber/asn1-ber v1.5.0/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
5+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
6+
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
7+
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
8+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
9+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
10+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
11+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

v3/bind.go

+24-3
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,8 @@ type NTLMBindRequest struct {
400400
Username string
401401
// Password is the credentials to bind with
402402
Password string
403+
// Hash is the hex NTLM hash to bind with. Password or hash must be provided
404+
Hash string
403405
// Controls are optional controls to send with the bind request
404406
Controls []Control
405407
}
@@ -441,9 +443,20 @@ func (l *Conn) NTLMBind(domain, username, password string) error {
441443
return err
442444
}
443445

446+
// NTLMBindWithHash performs an NTLM Bind with an NTLM hash instead of plaintext password (pass-the-hash)
447+
func (l *Conn) NTLMBindWithHash(domain, username, hash string) error {
448+
req := &NTLMBindRequest{
449+
Domain: domain,
450+
Username: username,
451+
Hash: hash,
452+
}
453+
_, err := l.NTLMChallengeBind(req)
454+
return err
455+
}
456+
444457
// NTLMChallengeBind performs the NTLMSSP bind operation defined in the given request
445458
func (l *Conn) NTLMChallengeBind(ntlmBindRequest *NTLMBindRequest) (*NTLMBindResult, error) {
446-
if ntlmBindRequest.Password == "" {
459+
if ntlmBindRequest.Password == "" && ntlmBindRequest.Hash == "" {
447460
return nil, NewError(ErrorEmptyPassword, errors.New("ldap: empty password not allowed by the client"))
448461
}
449462

@@ -481,8 +494,16 @@ func (l *Conn) NTLMChallengeBind(ntlmBindRequest *NTLMBindRequest) (*NTLMBindRes
481494
}
482495
}
483496
if ntlmsspChallenge != nil {
484-
// generate a response message to the challenge with the given Username/Password
485-
responseMessage, err := ntlmssp.ProcessChallenge(ntlmsspChallenge, ntlmBindRequest.Username, ntlmBindRequest.Password)
497+
var err error
498+
var responseMessage []byte
499+
// generate a response message to the challenge with the given Username/Password if password is provided
500+
if ntlmBindRequest.Password != "" {
501+
responseMessage, err = ntlmssp.ProcessChallenge(ntlmsspChallenge, ntlmBindRequest.Username, ntlmBindRequest.Password)
502+
} else if ntlmBindRequest.Hash != "" {
503+
responseMessage, err = ntlmssp.ProcessChallengeWithHash(ntlmsspChallenge, ntlmBindRequest.Username, ntlmBindRequest.Hash)
504+
} else {
505+
err = fmt.Errorf("need a password or hash to generate reply")
506+
}
486507
if err != nil {
487508
return result, fmt.Errorf("parsing ntlm-challenge: %s", err)
488509
}

v3/go.mod

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,8 @@ module github.com/go-ldap/ldap/v3
22

33
go 1.13
44

5-
require github.com/go-asn1-ber/asn1-ber v1.5.0
5+
require (
6+
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c
7+
github.com/go-asn1-ber/asn1-ber v1.5.0
8+
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 // indirect
9+
)

v3/go.sum

+9
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,11 @@
1+
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
2+
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
13
github.com/go-asn1-ber/asn1-ber v1.5.0 h1:/S4hO/AO6tLMlPX0oftGSOcdGJJN/MuYzfgWRMn199E=
24
github.com/go-asn1-ber/asn1-ber v1.5.0/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
5+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
6+
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
7+
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
8+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
9+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
10+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
11+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

0 commit comments

Comments
 (0)