Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authenticate via Kerberos with a CCache file #7

Merged
merged 6 commits into from
Oct 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 31 additions & 76 deletions Commands/RequestSPN.go
Original file line number Diff line number Diff line change
@@ -1,91 +1,46 @@
package Commands

import (
"encoding/hex"
"fmt"
"strings"
"encoding/hex"
"github.com/jcmturner/gokrb5/v8/config"
"github.com/jcmturner/gokrb5/v8/client"
"github.com/jcmturner/gokrb5/v8/iana/etypeID"
"log"
"os"
)

"ldapper/Globals"
"log"
"os"

// dont really like this string for the config
// would rather just create a new config and make changes via functions
// would be easier to read
// cant seem to figure out how to add a [realm] though
const (
libdefault = `[libdefaults]
default_realm = %s
dns_lookup_realm = false
dns_lookup_kdc = false
ticket_lifetime = 24h
renew_lifetime = 5
forwardable = yes
proxiable = true
default_tkt_enctypes = rc4-hmac
default_tgs_enctypes = rc4-hmac
noaddresses = true
udp_preference_limit=1
[realms]
%s = {
kdc = %s:88
default_domain = %s
}`
"github.com/jcmturner/gokrb5/v8/client"
"github.com/jcmturner/gokrb5/v8/iana/etypeID"
)

func RequestSPN(targetUser string, username string, password string, ntlm string, domain string, dc string, socksServer string, socksType int) (spnResult string) {

var cl *client.Client
var ticket string
func RequestSPN(targetUser string, username string, password string, ntlm string, domain string, dc string, ccache bool, socksServer string, socksType int) (spnResult string) {

// Need domain in uppercase for GOKRB5 Config
domain = strings.ToUpper(domain)
var cl *client.Client
var ticket string
var err error

l := log.New(os.Stderr, "GOKRB5 Client: ", log.Ldate|log.Ltime|log.Lshortfile)
cl = Globals.GetKerberosClient(domain, dc, username, password, ntlm, ccache, socksServer, socksType)

c, err := config.NewFromString(fmt.Sprintf(libdefault, domain, domain, dc, domain))
l := log.New(os.Stderr, "GOKRB5 Client: ", log.Ldate|log.Ltime|log.Lshortfile)

if err != nil {
l.Fatalf("Error Loading Config: %v\n", err)
}

// Create a Kerberos client with either password or hash
if password != ""{
cl = client.NewWithPassword(username, domain, password, c, client.DisablePAFXFAST(true), client.AssumePreAuthentication(false))
}else if ntlm != ""{
cl = client.NewWithHash(username, domain, ntlm, c, client.DisablePAFXFAST(true), client.AssumePreAuthentication(false))
}
err = cl.Login()
if err != nil {
l.Fatalf("Erron on AS_REQ: %v\n", err)
}

// Add socks info to client config if enabled
if socksServer != "" {
cl.Config.Socks.Enabled = true
cl.Config.Socks.Version = socksType
cl.Config.Socks.Server = socksServer
}
tgt, _, err := cl.GetMSPrincipalTicket(targetUser)

err = cl.Login()
if err != nil {
l.Fatalf("Erron on AS_REQ: %v\n", err)
}

tgt, _, err := cl.GetServiceTicket(targetUser)

// only printing out RC4 encrypted tickets currently
if err != nil {
l.Printf("Error getting service ticket: %v\n", err)
}else if tgt.EncPart.EType == etypeID.RC4_HMAC {
checksumHex := make([]byte, hex.EncodedLen(len(tgt.EncPart.Cipher[:16])))
hex.Encode(checksumHex, tgt.EncPart.Cipher[:16])
// only printing out RC4 encrypted tickets currently
if err != nil {
l.Printf("Error getting service ticket: %v\n", err)
} else if tgt.EncPart.EType == etypeID.RC4_HMAC {
checksumHex := make([]byte, hex.EncodedLen(len(tgt.EncPart.Cipher[:16])))
hex.Encode(checksumHex, tgt.EncPart.Cipher[:16])

cipherHex := make([]byte, hex.EncodedLen(len(tgt.EncPart.Cipher[16:])))
hex.Encode(cipherHex, tgt.EncPart.Cipher[16:])
ticket = fmt.Sprintf("$krb5tgs$%d$*%s$%s$%s*$%s$%s\n", tgt.EncPart.EType, tgt.SName.NameString[0], tgt.Realm, tgt.SName.NameString[0], checksumHex, cipherHex)
}else if tgt.EncPart.EType != etypeID.RC4_HMAC {
// Don't belive this would happen becuase we only offer rc4 encrpytion based on our config
l.Printf("Invalid encryption type")
}
return ticket
cipherHex := make([]byte, hex.EncodedLen(len(tgt.EncPart.Cipher[16:])))
hex.Encode(cipherHex, tgt.EncPart.Cipher[16:])
ticket = fmt.Sprintf("$krb5tgs$%d$*%s$%s$%s*$%s$%s\n", tgt.EncPart.EType, tgt.SName.NameString[0], tgt.Realm, tgt.SName.NameString[0], checksumHex, cipherHex)
} else if tgt.EncPart.EType != etypeID.RC4_HMAC {
// Don't belive this would happen becuase we only offer rc4 encrpytion based on our config
l.Printf("Invalid encryption type")
}
return ticket
}
102 changes: 102 additions & 0 deletions Globals/Globals.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,39 @@ import (
"io"
"log"
"math"
"net"
"os"
"strconv"
"strings"
"text/tabwriter"
"time"

"github.com/LeakIX/go-smb2"
"github.com/LeakIX/ntlmssp"
"github.com/go-ldap/ldap/v3"
"github.com/jcmturner/gokrb5/v8/client"
"github.com/jcmturner/gokrb5/v8/config"
"github.com/jcmturner/gokrb5/v8/credentials"
)

const (
libdefault = `[libdefaults]
default_realm = %s
dns_lookup_realm = false
dns_lookup_kdc = false
ticket_lifetime = 24h
renew_lifetime = 5
forwardable = yes
proxiable = true
default_tkt_enctypes = rc4-hmac
default_tgs_enctypes = rc4-hmac
noaddresses = true
udp_preference_limit=1
[realms]
%s = {
kdc = %s:88
default_domain = %s
}`
)

func LdapSearch(baseDN string, query string) *ldap.SearchRequest {
Expand Down Expand Up @@ -106,3 +132,79 @@ func GetArrayDifference(a, b []string) (diff []string) {

return
}

func GetMachineHostname(dc string, proxyDial func(string, string) (net.Conn, error)) string {
var conn net.Conn
var err error

if proxyDial != nil {
conn, err = proxyDial("tcp", fmt.Sprintf("%s:445", dc))
if err != nil {
panic(err)
}
} else {
conn, err = net.Dial("tcp", fmt.Sprintf("%s:445", dc))
if err != nil {
panic(err)
}
}

defer conn.Close()

ntlmsspClient, err := ntlmssp.NewClient(
ntlmssp.SetCompatibilityLevel(3),
ntlmssp.SetUserInfo("", ""),
ntlmssp.SetDomain(""))
if err != nil {
panic(err)
}
d := &smb2.Dialer{
Initiator: &smb2.NTLMSSPInitiator{
NTLMSSPClient: ntlmsspClient,
},
}

s, err := d.Dial(conn)
if err != nil {
log.Println(ntlmsspClient.SessionDetails().TargetInfo.Get(ntlmssp.MsvAvDNSComputerName))
panic(err)
}
dnsComputerName, _ := ntlmsspClient.SessionDetails().TargetInfo.Get(ntlmssp.MsvAvDNSComputerName)
defer s.Logoff()

dnsComputerNameString := string(dnsComputerName)
dnsComputerNameString = strings.Replace(dnsComputerNameString, "\x00", "", -1)

return dnsComputerNameString

}

func GetKerberosClient(domain string, dc string, username string, password string, ntlm string, ccacheAuth bool, socksAddress string, socksType int) *client.Client {

var cl *client.Client
var err error

domain = strings.ToUpper(domain)
c, _ := config.NewFromString(fmt.Sprintf(libdefault, domain, domain, dc, domain))

if ccacheAuth {
ccache, _ := credentials.LoadCCache(os.Getenv("KRB5CCNAME"))
cl, err = client.NewFromCCache(ccache, c)
if err != nil {
log.Fatal(err)
}
} else if password != "" {
cl = client.NewWithPassword(username, domain, password, c, client.DisablePAFXFAST(true), client.AssumePreAuthentication(false))
} else if ntlm != "" {
cl = client.NewWithHash(username, domain, ntlm, c, client.DisablePAFXFAST(true), client.AssumePreAuthentication(false))
}

if socksAddress != "" {
cl.Config.Socks.Enabled = true
cl.Config.Socks.Version = socksType
cl.Config.Socks.Server = socksAddress
}

return cl

}
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ Usage of ./ldapper:
Log file
-p string
Password
-k Use Kerberos authentication
-s Bind using LDAPS
-socks4 string
SOCKS4 Proxy Address (ip:port)
Expand All @@ -82,6 +83,7 @@ Usage of ./ldapper:
Examples:
With Password: ./ldapper -u <username@domain> -p <password> -dc <ip/FQDN> -s
With Hash: ./ldapper -u <username@domain> -H <hash> -dc <ip/FQDN> -s
With Kerberos: ./ldapper -u <username@domain> -k -dc <ip/FQDN> -s
```

# LDAPS Support
Expand All @@ -106,6 +108,14 @@ Ldapper can also authenticate with a user's NTLM hash. This method can be used w
> ./ldapper -u 'hanzo@overwatch.local' -H OOGNKVJB2TRCYLD26H4DVPF3KBP0SG03 -dc 10.10.10.101 -s
```

## Kerberos

Ldapper can also authenticate using a CCache file specefied in the KRB5CCNAME enviroment variable with the `-k` flag.

```
> ./ldapper -u 'hanzo@overwatch.local' -k -dc 10.10.10.101 -s
```

# Query Modules

## Net
Expand Down
8 changes: 7 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ module ldapper
go 1.17

require (
github.com/LeakIX/go-smb2 v1.2.0
github.com/LeakIX/ntlmssp v0.0.0-20220417170740-7da3d6bf7333
github.com/go-ldap/ldap/v3 v3.4.4
github.com/jcmturner/gokrb5/v8 v8.4.3
github.com/mazen160/go-random v0.0.0-20210308102632-d2b501c85c03
Expand All @@ -12,14 +14,18 @@ require (

require (
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
github.com/geoffgarside/ber v1.1.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
github.com/jcmturner/gofork v1.7.6 // indirect
github.com/jcmturner/goidentity/v6 v6.0.1 // indirect
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/net v0.0.0-20220725212005-46097bf591d3 // indirect
)

replace github.com/jcmturner/gokrb5/v8 => github.com/mfdooom/gokrb5/v8 v8.4.3-0.20220811043259-08c37c0bdf17
replace github.com/go-ldap/ldap/v3 => github.com/mfdooom/ldap/v3 v3.0.0-20221002192048-71cd843f12e2

replace github.com/jcmturner/gokrb5/v8 => github.com/mfdooom/gokrb5/v8 v8.4.3-0.20220930223631-56e96234d4e0
Loading