nuclei/pkg/js/libs/kerberos/kerberos.go

194 lines
4.9 KiB
Go
Raw Normal View History

javascript protocol for scripting (includes 15+ proto libs) (#4109) * rebase js-layer PR from @ice3man543 * package restructuring * working * fix duplicated event & matcher status * fix lint error * fix response field * add new functions * multiple minor improvements * fix incorrect stats in js protocol * sort output metadata in cli * remove temp files * remove dead code * add unit and integration test * fix lint error * add jsdoclint using llm * fix error in test * add js lint using llm * generate docs of libs * llm lint * remove duplicated docs * update generated docs * update prompt in doclint * update docs * temp disable version check test * fix unit test and add retry * fix panic in it * update and move jsdocs * updated jsdocs * update docs * update container platform in test * dir restructure and adding docs * add api_reference and remove markdown docs * fix imports * add javascript design and contribution docs * add js protocol documentation * update integration test and docs * update doc ext mdx->md * minor update to docs * new integration test and more * move go libs and add docs * gen new net docs and more * final docs update * add new devtool * use fastdialer * fix build fail * use fastdialer + network sandbox support * add reserved keyword 'Port' * update Port to new syntax * misc update * always enable templatectx in js protocol * move docs to 'js-proto-docs' repo * remove scrapefuncs binary --------- Co-authored-by: sandeep <8293321+ehsandeep@users.noreply.github.com>
2023-09-16 16:02:17 +05:30
package kerberos
import (
"encoding/hex"
"fmt"
"html/template"
"strings"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
javascript protocol for scripting (includes 15+ proto libs) (#4109) * rebase js-layer PR from @ice3man543 * package restructuring * working * fix duplicated event & matcher status * fix lint error * fix response field * add new functions * multiple minor improvements * fix incorrect stats in js protocol * sort output metadata in cli * remove temp files * remove dead code * add unit and integration test * fix lint error * add jsdoclint using llm * fix error in test * add js lint using llm * generate docs of libs * llm lint * remove duplicated docs * update generated docs * update prompt in doclint * update docs * temp disable version check test * fix unit test and add retry * fix panic in it * update and move jsdocs * updated jsdocs * update docs * update container platform in test * dir restructure and adding docs * add api_reference and remove markdown docs * fix imports * add javascript design and contribution docs * add js protocol documentation * update integration test and docs * update doc ext mdx->md * minor update to docs * new integration test and more * move go libs and add docs * gen new net docs and more * final docs update * add new devtool * use fastdialer * fix build fail * use fastdialer + network sandbox support * add reserved keyword 'Port' * update Port to new syntax * misc update * always enable templatectx in js protocol * move docs to 'js-proto-docs' repo * remove scrapefuncs binary --------- Co-authored-by: sandeep <8293321+ehsandeep@users.noreply.github.com>
2023-09-16 16:02:17 +05:30
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))
defer cl.Destroy()
javascript protocol for scripting (includes 15+ proto libs) (#4109) * rebase js-layer PR from @ice3man543 * package restructuring * working * fix duplicated event & matcher status * fix lint error * fix response field * add new functions * multiple minor improvements * fix incorrect stats in js protocol * sort output metadata in cli * remove temp files * remove dead code * add unit and integration test * fix lint error * add jsdoclint using llm * fix error in test * add js lint using llm * generate docs of libs * llm lint * remove duplicated docs * update generated docs * update prompt in doclint * update docs * temp disable version check test * fix unit test and add retry * fix panic in it * update and move jsdocs * updated jsdocs * update docs * update container platform in test * dir restructure and adding docs * add api_reference and remove markdown docs * fix imports * add javascript design and contribution docs * add js protocol documentation * update integration test and docs * update doc ext mdx->md * minor update to docs * new integration test and more * move go libs and add docs * gen new net docs and more * final docs update * add new devtool * use fastdialer * fix build fail * use fastdialer + network sandbox support * add reserved keyword 'Port' * update Port to new syntax * misc update * always enable templatectx in js protocol * move docs to 'js-proto-docs' repo * remove scrapefuncs binary --------- Co-authored-by: sandeep <8293321+ehsandeep@users.noreply.github.com>
2023-09-16 16:02:17 +05:30
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
}
type TGS struct {
Ticket messages.Ticket
Hash string
}
func (c *KerberosClient) GetServiceTicket(domain, controller string, username, password string, target, spn string) (TGS, error) {
var tgs TGS
if !protocolstate.IsHostAllowed(domain) {
// host is not valid according to network policy
return tgs, protocolstate.ErrHostDenied.Msgf(domain)
}
opts, err := newKerbrosEnumUserOpts(domain, controller)
if err != nil {
return tgs, err
}
cl := kclient.NewWithPassword(username, opts.realm, password, opts.config, kclient.DisablePAFXFAST(true))
defer cl.Destroy()
ticket, _, err := cl.GetServiceTicket(spn)
if err != nil {
return tgs, err
}
hashcat, err := tgsToHashcat(ticket, target)
if err != nil {
return tgs, err
}
return TGS{
Ticket: ticket,
Hash: hashcat,
}, nil
}
func tgsToHashcat(tgs messages.Ticket, username string) (string, error) {
return fmt.Sprintf("$krb5tgs$%d$*%s$%s$%s*$%s$%s",
tgs.EncPart.EType,
username,
tgs.Realm,
strings.Join(tgs.SName.NameString[:], "/"),
hex.EncodeToString(tgs.EncPart.Cipher[:16]),
hex.EncodeToString(tgs.EncPart.Cipher[16:]),
), nil
}