Skip to content

Commit

Permalink
Merge pull request #4667 from 5amu/ldap-protocol-enhancements
Browse files Browse the repository at this point in the history
ldap protocol enhancements
  • Loading branch information
tarunKoyalwar committed Feb 5, 2024
2 parents 3b75db4 + 66bc616 commit 7647af1
Show file tree
Hide file tree
Showing 5 changed files with 513 additions and 233 deletions.
44 changes: 40 additions & 4 deletions pkg/js/generated/go/libldap/ldap.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,52 @@ func init() {
module.Set(
gojs.Objects{
// Functions
"DecodeADTimestamp": lib_ldap.DecodeADTimestamp,
"DecodeSID": lib_ldap.DecodeSID,
"DecodeZuluTimestamp": lib_ldap.DecodeZuluTimestamp,
"JoinFilters": lib_ldap.JoinFilters,
"NegativeFilter": lib_ldap.NegativeFilter,

// Var and consts
"FilterAccountDisabled": lib_ldap.FilterAccountDisabled,
"FilterAccountEnabled": lib_ldap.FilterAccountEnabled,
"FilterCanSendEncryptedPassword": lib_ldap.FilterCanSendEncryptedPassword,
"FilterDontExpirePassword": lib_ldap.FilterDontExpirePassword,
"FilterDontRequirePreauth": lib_ldap.FilterDontRequirePreauth,
"FilterHasServicePrincipalName": lib_ldap.FilterHasServicePrincipalName,
"FilterHomedirRequired": lib_ldap.FilterHomedirRequired,
"FilterInterdomainTrustAccount": lib_ldap.FilterInterdomainTrustAccount,
"FilterIsAdmin": lib_ldap.FilterIsAdmin,
"FilterIsComputer": lib_ldap.FilterIsComputer,
"FilterIsDuplicateAccount": lib_ldap.FilterIsDuplicateAccount,
"FilterIsGroup": lib_ldap.FilterIsGroup,
"FilterIsNormalAccount": lib_ldap.FilterIsNormalAccount,
"FilterIsPerson": lib_ldap.FilterIsPerson,
"FilterLockout": lib_ldap.FilterLockout,
"FilterLogonScript": lib_ldap.FilterLogonScript,
"FilterMnsLogonAccount": lib_ldap.FilterMnsLogonAccount,
"FilterNotDelegated": lib_ldap.FilterNotDelegated,
"FilterPartialSecretsAccount": lib_ldap.FilterPartialSecretsAccount,
"FilterPasswordCantChange": lib_ldap.FilterPasswordCantChange,
"FilterPasswordExpired": lib_ldap.FilterPasswordExpired,
"FilterPasswordNotRequired": lib_ldap.FilterPasswordNotRequired,
"FilterServerTrustAccount": lib_ldap.FilterServerTrustAccount,
"FilterSmartCardRequired": lib_ldap.FilterSmartCardRequired,
"FilterTrustedForDelegation": lib_ldap.FilterTrustedForDelegation,
"FilterTrustedToAuthForDelegation": lib_ldap.FilterTrustedToAuthForDelegation,
"FilterUseDesKeyOnly": lib_ldap.FilterUseDesKeyOnly,
"FilterWorkstationTrustAccount": lib_ldap.FilterWorkstationTrustAccount,

// Types (value type)
"LDAPMetadata": func() lib_ldap.LDAPMetadata { return lib_ldap.LDAPMetadata{} },
"LdapClient": func() lib_ldap.LdapClient { return lib_ldap.LdapClient{} },
"ADObject": func() lib_ldap.ADObject { return lib_ldap.ADObject{} },
"Client": lib_ldap.NewClient,
"Config": func() lib_ldap.Config { return lib_ldap.Config{} },
"Metadata": func() lib_ldap.Metadata { return lib_ldap.Metadata{} },

// Types (pointer type)
"NewLDAPMetadata": func() *lib_ldap.LDAPMetadata { return &lib_ldap.LDAPMetadata{} },
"NewLdapClient": func() *lib_ldap.LdapClient { return &lib_ldap.LdapClient{} },
"NewADObject": func() *lib_ldap.ADObject { return &lib_ldap.ADObject{} },
"NewConfig": func() *lib_ldap.Config { return &lib_ldap.Config{} },
"NewMetadata": func() *lib_ldap.Metadata { return &lib_ldap.Metadata{} },
},
).Register()
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/js/libs/kerberos/kerberosx.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (c *Config) SetTimeout(timeout int) *Config {
// DomainController: dc.acme.com (Domain Controller / Active Directory Server)
// KDC: kdc.acme.com (Key Distribution Center / Authentication Server)

// Updated Package definations and structure
// Client is kerberos client
type Client struct {
nj *utils.NucleiJS // helper functions/bindings
Krb5Config *kconfig.Config
Expand All @@ -71,7 +71,7 @@ type Client struct {
func NewKerberosClient(call goja.ConstructorCall, runtime *goja.Runtime) *goja.Object {
// setup nucleijs utils
c := &Client{nj: utils.NewNucleiJS(runtime)}
c.nj.ObjectSig = "Client(domain, controller)" // will be included in error messages
c.nj.ObjectSig = "Client(domain, {controller})" // will be included in error messages

// get arguments (type assertion is efficient than reflection)
// when accepting type as input like net.Conn we can use utils.GetArg
Expand Down
185 changes: 185 additions & 0 deletions pkg/js/libs/ldap/adenum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package ldap

import (
"fmt"
"strings"

"github.com/go-ldap/ldap/v3"
)

// LDAP makes you search using an OID
// http://oid-info.com/get/1.2.840.113556.1.4.803
//
// The one for the userAccountControl in MS Active Directory is
// 1.2.840.113556.1.4.803 (LDAP_MATCHING_RULE_BIT_AND)
//
// We can look at the enabled flags using a query like (!(userAccountControl:1.2.840.113556.1.4.803:=2))
//
// https://learn.microsoft.com/en-us/troubleshoot/windows-server/identity/useraccountcontrol-manipulate-account-properties
const (
FilterIsPerson = "(objectCategory=person)"
FilterIsGroup = "(objectCategory=group)"
FilterIsComputer = "(objectCategory=computer)"
FilterIsAdmin = "(adminCount=1)"
FilterHasServicePrincipalName = "(servicePrincipalName=*)"
FilterLogonScript = "(userAccountControl:1.2.840.113556.1.4.803:=1)" // The logon script will be run.
FilterAccountDisabled = "(userAccountControl:1.2.840.113556.1.4.803:=2)" // The user account is disabled.
FilterAccountEnabled = "(!(userAccountControl:1.2.840.113556.1.4.803:=2))" // The user account is enabled.
FilterHomedirRequired = "(userAccountControl:1.2.840.113556.1.4.803:=8)" // The home folder is required.
FilterLockout = "(userAccountControl:1.2.840.113556.1.4.803:=16)" // The user is locked out.
FilterPasswordNotRequired = "(userAccountControl:1.2.840.113556.1.4.803:=32)" // No password is required.
FilterPasswordCantChange = "(userAccountControl:1.2.840.113556.1.4.803:=64)" // The user can't change the password.
FilterCanSendEncryptedPassword = "(userAccountControl:1.2.840.113556.1.4.803:=128)" // The user can send an encrypted password.
FilterIsDuplicateAccount = "(userAccountControl:1.2.840.113556.1.4.803:=256)" // It's an account for users whose primary account is in another domain.
FilterIsNormalAccount = "(userAccountControl:1.2.840.113556.1.4.803:=512)" // It's a default account type that represents a typical user.
FilterInterdomainTrustAccount = "(userAccountControl:1.2.840.113556.1.4.803:=2048)" // It's a permit to trust an account for a system domain that trusts other domains.
FilterWorkstationTrustAccount = "(userAccountControl:1.2.840.113556.1.4.803:=4096)" // It's a computer account for a computer that is running old Windows builds.
FilterServerTrustAccount = "(userAccountControl:1.2.840.113556.1.4.803:=8192)" // It's a computer account for a domain controller that is a member of this domain.
FilterDontExpirePassword = "(userAccountControl:1.2.840.113556.1.4.803:=65536)" // Represents the password, which should never expire on the account.
FilterMnsLogonAccount = "(userAccountControl:1.2.840.113556.1.4.803:=131072)" // It's an MNS logon account.
FilterSmartCardRequired = "(userAccountControl:1.2.840.113556.1.4.803:=262144)" // When this flag is set, it forces the user to log on by using a smart card.
FilterTrustedForDelegation = "(userAccountControl:1.2.840.113556.1.4.803:=524288)" // When this flag is set, the service account (the user or computer account) under which a service runs is trusted for Kerberos delegation.
FilterNotDelegated = "(userAccountControl:1.2.840.113556.1.4.803:=1048576)" // When this flag is set, the security context of the user isn't delegated to a service even if the service account is set as trusted for Kerberos delegation.
FilterUseDesKeyOnly = "(userAccountControl:1.2.840.113556.1.4.803:=2097152)" // Restrict this principal to use only Data Encryption Standard (DES) encryption types for keys.
FilterDontRequirePreauth = "(userAccountControl:1.2.840.113556.1.4.803:=4194304)" // This account doesn't require Kerberos pre-authentication for logging on.
FilterPasswordExpired = "(userAccountControl:1.2.840.113556.1.4.803:=8388608)" // The user's password has expired.
FilterTrustedToAuthForDelegation = "(userAccountControl:1.2.840.113556.1.4.803:=16777216)" // The account is enabled for delegation.
FilterPartialSecretsAccount = "(userAccountControl:1.2.840.113556.1.4.803:=67108864)" // The account is a read-only domain controller (RODC).

)

// JoinFilters joins multiple filters into a single filter
func JoinFilters(filters ...string) string {
var builder strings.Builder
builder.WriteString("(&")
for _, s := range filters {
builder.WriteString(s)
}
builder.WriteString(")")
return builder.String()
}

// NegativeFilter returns a negative filter for a given filter
func NegativeFilter(filter string) string {
return fmt.Sprintf("(!%s)", filter)
}

// ADObject represents an Active Directory object
type ADObject struct {
DistinguishedName string
SAMAccountName string
PWDLastSet string
LastLogon string
MemberOf []string
ServicePrincipalName []string
}

// FindADObjects finds AD objects based on a filter
// and returns them as a list of ADObject
// @param filter: string
// @return []ADObject
func (c *Client) FindADObjects(filter string) []ADObject {
c.nj.Require(c.conn != nil, "no existing connection")
sr := ldap.NewSearchRequest(
c.BaseDN, ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases, 0, 0, false,
filter,
[]string{
"distinguishedName",
"sAMAccountName",
"pwdLastSet",
"lastLogon",
"memberOf",
"servicePrincipalName",
},
nil,
)

res, err := c.conn.Search(sr)
c.nj.HandleError(err, "ldap search request failed")

var objects []ADObject
for _, obj := range res.Entries {
objects = append(objects, ADObject{
DistinguishedName: obj.GetAttributeValue("distinguishedName"),
SAMAccountName: obj.GetAttributeValue("sAMAccountName"),
PWDLastSet: DecodeADTimestamp(obj.GetAttributeValue("pwdLastSet")),
LastLogon: DecodeADTimestamp(obj.GetAttributeValue("lastLogon")),
MemberOf: obj.GetAttributeValues("memberOf"),
ServicePrincipalName: obj.GetAttributeValues("servicePrincipalName"),
})
}
return objects
}

// GetADUsers returns all AD users
// using FilterIsPerson filter query
// @return []ADObject
func (c *Client) GetADUsers() []ADObject {
return c.FindADObjects(FilterIsPerson)
}

// GetADActiveUsers returns all AD users
// using FilterIsPerson and FilterAccountEnabled filter query
// @return []ADObject
func (c *Client) GetADActiveUsers() []ADObject {
return c.FindADObjects(JoinFilters(FilterIsPerson, FilterAccountEnabled))
}

// GetAdUserWithNeverExpiringPasswords returns all AD users
// using FilterIsPerson and FilterDontExpirePassword filter query
// @return []ADObject
func (c *Client) GetADUserWithNeverExpiringPasswords() []ADObject {
return c.FindADObjects(JoinFilters(FilterIsPerson, FilterDontExpirePassword))
}

// GetADUserTrustedForDelegation returns all AD users that are trusted for delegation
// using FilterIsPerson and FilterTrustedForDelegation filter query
// @return []ADObject
func (c *Client) GetADUserTrustedForDelegation() []ADObject {
return c.FindADObjects(JoinFilters(FilterIsPerson, FilterTrustedForDelegation))
}

// GetADUserWithPasswordNotRequired returns all AD users that do not require a password
// using FilterIsPerson and FilterPasswordNotRequired filter query
// @return []ADObject
func (c *Client) GetADUserWithPasswordNotRequired() []ADObject {
return c.FindADObjects(JoinFilters(FilterIsPerson, FilterPasswordNotRequired))
}

// GetADGroups returns all AD groups
// using FilterIsGroup filter query
// @return []ADObject
func (c *Client) GetADGroups() []ADObject {
return c.FindADObjects(FilterIsGroup)
}

// GetADDCList returns all AD domain controllers
// using FilterIsComputer, FilterAccountEnabled and FilterServerTrustAccount filter query
// @return []ADObject
func (c *Client) GetADDCList() []ADObject {
return c.FindADObjects(JoinFilters(FilterIsComputer, FilterAccountEnabled, FilterServerTrustAccount))
}

// GetADAdmins returns all AD admins
// using FilterIsPerson, FilterAccountEnabled and FilterIsAdmin filter query
// @return []ADObject
func (c *Client) GetADAdmins() []ADObject {
return c.FindADObjects(JoinFilters(FilterIsPerson, FilterAccountEnabled, FilterIsAdmin))
}

// GetADUserKerberoastable returns all AD users that are kerberoastable
// using FilterIsPerson, FilterAccountEnabled and FilterHasServicePrincipalName filter query
// @return []ADObject
func (c *Client) GetADUserKerberoastable() []ADObject {
return c.FindADObjects(JoinFilters(FilterIsPerson, FilterAccountEnabled, FilterHasServicePrincipalName))
}

// GetADDomainSID returns the SID of the AD domain
// @return string
func (c *Client) GetADDomainSID() string {
r := c.Search(FilterServerTrustAccount, "objectSid")
c.nj.Require(len(r) > 0, "no result from GetADDomainSID query")
c.nj.Require(len(r[0]["objectSid"]) > 0, "could not grab DomainSID")
return DecodeSID(r[0]["objectSid"][0])
}
Loading

0 comments on commit 7647af1

Please sign in to comment.