-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
kerberos.go
145 lines (128 loc) · 3.75 KB
/
kerberos.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package kerberos
import (
"encoding/hex"
"fmt"
"html/template"
"strings"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
kclient "github.com/ropnop/gokrb5/v8/client"
kconfig "github.com/ropnop/gokrb5/v8/config"
"github.com/ropnop/gokrb5/v8/iana/errorcode"
"github.com/ropnop/gokrb5/v8/messages"
)
// Client is a kerberos client
type KerberosClient struct{}
type kerberosEnumUserOpts struct {
realm string
config *kconfig.Config
kdcs map[int]string
}
// Taken from kerbrute: https://github.com/ropnop/kerbrute/blob/master/session/session.go
const krb5ConfigTemplateDNS = `[libdefaults]
dns_lookup_kdc = true
default_realm = {{.Realm}}
`
const krb5ConfigTemplateKDC = `[libdefaults]
default_realm = {{.Realm}}
[realms]
{{.Realm}} = {
kdc = {{.DomainController}}
admin_server = {{.DomainController}}
}
`
func buildKrb5Template(realm, domainController string) string {
data := map[string]interface{}{
"Realm": realm,
"DomainController": domainController,
}
var kTemplate string
if domainController == "" {
kTemplate = krb5ConfigTemplateDNS
} else {
kTemplate = krb5ConfigTemplateKDC
}
t := template.Must(template.New("krb5ConfigString").Parse(kTemplate))
builder := &strings.Builder{}
if err := t.Execute(builder, data); err != nil {
panic(err)
}
return builder.String()
}
func newKerbrosEnumUserOpts(domain, domainController string) (*kerberosEnumUserOpts, error) {
realm := strings.ToUpper(domain)
configstring := buildKrb5Template(realm, domainController)
Config, err := kconfig.NewFromString(configstring)
if err != nil {
return nil, err
}
_, kdcs, err := Config.GetKDCs(realm, false)
if err != nil {
err = fmt.Errorf("couldn't find any KDCs for realm %s. Please specify a Domain Controller", realm)
return nil, err
}
return &kerberosEnumUserOpts{realm: realm, config: Config, kdcs: kdcs}, nil
}
// EnumerateUserResponse is the response from EnumerateUser
type EnumerateUserResponse struct {
Valid bool
ASREPHash string
}
// EnumerateUser returns true if the user exists in the domain
//
// If the user is not found, false is returned.
// If the user is found, true is returned. Optionally, the AS-REP
// hash is also returned if discovered.
func (c *KerberosClient) EnumerateUser(domain, controller string, username string) (EnumerateUserResponse, error) {
resp := EnumerateUserResponse{}
if !protocolstate.IsHostAllowed(domain) {
// host is not valid according to network policy
return resp, protocolstate.ErrHostDenied.Msgf(domain)
}
opts, err := newKerbrosEnumUserOpts(domain, controller)
if err != nil {
return resp, err
}
cl := kclient.NewWithPassword(username, opts.realm, "foobar", opts.config, kclient.DisablePAFXFAST(true))
req, err := messages.NewASReqForTGT(cl.Credentials.Domain(), cl.Config, cl.Credentials.CName())
if err != nil {
return resp, err
}
b, err := req.Marshal()
if err != nil {
return resp, err
}
rb, err := cl.SendToKDC(b, opts.realm)
if err == nil {
var ASRep messages.ASRep
err = ASRep.Unmarshal(rb)
if err != nil {
// something went wrong, it's not a valid response
return resp, err
}
hashcatString, _ := asRepToHashcat(ASRep)
resp.Valid = true
resp.ASREPHash = hashcatString
return resp, nil
}
e, ok := err.(messages.KRBError)
if !ok {
return resp, nil
}
switch e.ErrorCode {
case errorcode.KDC_ERR_C_PRINCIPAL_UNKNOWN:
return resp, nil
case errorcode.KDC_ERR_PREAUTH_REQUIRED:
resp.Valid = true
return resp, nil
default:
return resp, err
}
}
func asRepToHashcat(asrep messages.ASRep) (string, error) {
return fmt.Sprintf("$krb5asrep$%d$%s@%s:%s$%s",
asrep.EncPart.EType,
asrep.CName.PrincipalNameString(),
asrep.CRealm,
hex.EncodeToString(asrep.EncPart.Cipher[:16]),
hex.EncodeToString(asrep.EncPart.Cipher[16:])), nil
}