package ldap12import (3"fmt"4"strings"56"github.com/go-ldap/ldap/v3"7)89// LDAP makes you search using an OID10// http://oid-info.com/get/1.2.840.113556.1.4.80311//12// The one for the userAccountControl in MS Active Directory is13// 1.2.840.113556.1.4.803 (LDAP_MATCHING_RULE_BIT_AND)14//15// We can look at the enabled flags using a query like (!(userAccountControl:1.2.840.113556.1.4.803:=2))16//17// https://learn.microsoft.com/en-us/troubleshoot/windows-server/identity/useraccountcontrol-manipulate-account-properties18const (19FilterIsPerson = "(objectCategory=person)" // The object is a person.20FilterIsGroup = "(objectCategory=group)" // The object is a group.21FilterIsComputer = "(objectCategory=computer)" // The object is a computer.22FilterIsAdmin = "(adminCount=1)" // The object is an admin.23FilterHasServicePrincipalName = "(servicePrincipalName=*)" // The object has a service principal name.24FilterLogonScript = "(userAccountControl:1.2.840.113556.1.4.803:=1)" // The logon script will be run.25FilterAccountDisabled = "(userAccountControl:1.2.840.113556.1.4.803:=2)" // The user account is disabled.26FilterAccountEnabled = "(!(userAccountControl:1.2.840.113556.1.4.803:=2))" // The user account is enabled.27FilterHomedirRequired = "(userAccountControl:1.2.840.113556.1.4.803:=8)" // The home folder is required.28FilterLockout = "(userAccountControl:1.2.840.113556.1.4.803:=16)" // The user is locked out.29FilterPasswordNotRequired = "(userAccountControl:1.2.840.113556.1.4.803:=32)" // No password is required.30FilterPasswordCantChange = "(userAccountControl:1.2.840.113556.1.4.803:=64)" // The user can't change the password.31FilterCanSendEncryptedPassword = "(userAccountControl:1.2.840.113556.1.4.803:=128)" // The user can send an encrypted password.32FilterIsDuplicateAccount = "(userAccountControl:1.2.840.113556.1.4.803:=256)" // It's an account for users whose primary account is in another domain.33FilterIsNormalAccount = "(userAccountControl:1.2.840.113556.1.4.803:=512)" // It's a default account type that represents a typical user.34FilterInterdomainTrustAccount = "(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.35FilterWorkstationTrustAccount = "(userAccountControl:1.2.840.113556.1.4.803:=4096)" // It's a computer account for a computer that is running old Windows builds.36FilterServerTrustAccount = "(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.37FilterDontExpirePassword = "(userAccountControl:1.2.840.113556.1.4.803:=65536)" // Represents the password, which should never expire on the account.38FilterMnsLogonAccount = "(userAccountControl:1.2.840.113556.1.4.803:=131072)" // It's an MNS logon account.39FilterSmartCardRequired = "(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.40FilterTrustedForDelegation = "(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.41FilterNotDelegated = "(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.42FilterUseDesKeyOnly = "(userAccountControl:1.2.840.113556.1.4.803:=2097152)" // Restrict this principal to use only Data Encryption Standard (DES) encryption types for keys.43FilterDontRequirePreauth = "(userAccountControl:1.2.840.113556.1.4.803:=4194304)" // This account doesn't require Kerberos pre-authentication for logging on.44FilterPasswordExpired = "(userAccountControl:1.2.840.113556.1.4.803:=8388608)" // The user's password has expired.45FilterTrustedToAuthForDelegation = "(userAccountControl:1.2.840.113556.1.4.803:=16777216)" // The account is enabled for delegation.46FilterPartialSecretsAccount = "(userAccountControl:1.2.840.113556.1.4.803:=67108864)" // The account is a read-only domain controller (RODC).4748)4950// JoinFilters joins multiple filters into a single filter51// @example52// ```javascript53// const ldap = require('nuclei/ldap');54// const filter = ldap.JoinFilters(ldap.FilterIsPerson, ldap.FilterAccountEnabled);55// ```56func JoinFilters(filters ...string) string {57var builder strings.Builder58builder.WriteString("(&")59for _, s := range filters {60builder.WriteString(s)61}62builder.WriteString(")")63return builder.String()64}6566// NegativeFilter returns a negative filter for a given filter67// @example68// ```javascript69// const ldap = require('nuclei/ldap');70// const filter = ldap.NegativeFilter(ldap.FilterIsPerson);71// ```72func NegativeFilter(filter string) string {73return fmt.Sprintf("(!%s)", filter)74}7576// FindADObjects finds AD objects based on a filter77// and returns them as a list of ADObject78// @example79// ```javascript80// const ldap = require('nuclei/ldap');81// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');82// const users = client.FindADObjects(ldap.FilterIsPerson);83// log(to_json(users));84// ```85func (c *Client) FindADObjects(filter string) SearchResult {86c.nj.Require(c.conn != nil, "no existing connection")87sr := ldap.NewSearchRequest(88c.BaseDN, ldap.ScopeWholeSubtree,89ldap.NeverDerefAliases, 0, 0, false,90filter,91[]string{92"distinguishedName",93"sAMAccountName",94"pwdLastSet",95"lastLogon",96"memberOf",97"servicePrincipalName",98},99nil,100)101102res, err := c.conn.Search(sr)103c.nj.HandleError(err, "ldap search request failed")104return *getSearchResult(res)105}106107// GetADUsers returns all AD users108// using FilterIsPerson filter query109// @example110// ```javascript111// const ldap = require('nuclei/ldap');112// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');113// const users = client.GetADUsers();114// log(to_json(users));115// ```116func (c *Client) GetADUsers() SearchResult {117return c.FindADObjects(FilterIsPerson)118}119120// GetADActiveUsers returns all AD users121// using FilterIsPerson and FilterAccountEnabled filter query122// @example123// ```javascript124// const ldap = require('nuclei/ldap');125// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');126// const users = client.GetADActiveUsers();127// log(to_json(users));128// ```129func (c *Client) GetADActiveUsers() SearchResult {130return c.FindADObjects(JoinFilters(FilterIsPerson, FilterAccountEnabled))131}132133// GetAdUserWithNeverExpiringPasswords returns all AD users134// using FilterIsPerson and FilterDontExpirePassword filter query135// @example136// ```javascript137// const ldap = require('nuclei/ldap');138// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');139// const users = client.GetADUserWithNeverExpiringPasswords();140// log(to_json(users));141// ```142func (c *Client) GetADUserWithNeverExpiringPasswords() SearchResult {143return c.FindADObjects(JoinFilters(FilterIsPerson, FilterDontExpirePassword))144}145146// GetADUserTrustedForDelegation returns all AD users that are trusted for delegation147// using FilterIsPerson and FilterTrustedForDelegation filter query148// @example149// ```javascript150// const ldap = require('nuclei/ldap');151// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');152// const users = client.GetADUserTrustedForDelegation();153// log(to_json(users));154// ```155func (c *Client) GetADUserTrustedForDelegation() SearchResult {156return c.FindADObjects(JoinFilters(FilterIsPerson, FilterTrustedForDelegation))157}158159// GetADUserWithPasswordNotRequired returns all AD users that do not require a password160// using FilterIsPerson and FilterPasswordNotRequired filter query161// @example162// ```javascript163// const ldap = require('nuclei/ldap');164// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');165// const users = client.GetADUserWithPasswordNotRequired();166// log(to_json(users));167// ```168func (c *Client) GetADUserWithPasswordNotRequired() SearchResult {169return c.FindADObjects(JoinFilters(FilterIsPerson, FilterPasswordNotRequired))170}171172// GetADGroups returns all AD groups173// using FilterIsGroup filter query174// @example175// ```javascript176// const ldap = require('nuclei/ldap');177// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');178// const groups = client.GetADGroups();179// log(to_json(groups));180// ```181func (c *Client) GetADGroups() SearchResult {182return c.FindADObjects(FilterIsGroup)183}184185// GetADDCList returns all AD domain controllers186// using FilterIsComputer, FilterAccountEnabled and FilterServerTrustAccount filter query187// @example188// ```javascript189// const ldap = require('nuclei/ldap');190// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');191// const dcs = client.GetADDCList();192// log(to_json(dcs));193// ```194func (c *Client) GetADDCList() SearchResult {195return c.FindADObjects(JoinFilters(FilterIsComputer, FilterAccountEnabled, FilterServerTrustAccount))196}197198// GetADAdmins returns all AD admins199// using FilterIsPerson, FilterAccountEnabled and FilterIsAdmin filter query200// @example201// ```javascript202// const ldap = require('nuclei/ldap');203// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');204// const admins = client.GetADAdmins();205// log(to_json(admins));206// ```207func (c *Client) GetADAdmins() SearchResult {208return c.FindADObjects(JoinFilters(FilterIsPerson, FilterAccountEnabled, FilterIsAdmin))209}210211// GetADUserKerberoastable returns all AD users that are kerberoastable212// using FilterIsPerson, FilterAccountEnabled and FilterHasServicePrincipalName filter query213// @example214// ```javascript215// const ldap = require('nuclei/ldap');216// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');217// const kerberoastable = client.GetADUserKerberoastable();218// log(to_json(kerberoastable));219// ```220func (c *Client) GetADUserKerberoastable() SearchResult {221return c.FindADObjects(JoinFilters(FilterIsPerson, FilterAccountEnabled, FilterHasServicePrincipalName))222}223224// GetADUserAsRepRoastable returns all AD users that are AsRepRoastable225// using FilterIsPerson, and FilterDontRequirePreauth filter query226// @example227// ```javascript228// const ldap = require('nuclei/ldap');229// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');230// const AsRepRoastable = client.GetADUserAsRepRoastable();231// log(to_json(AsRepRoastable));232// ```233func (c *Client) GetADUserAsRepRoastable() SearchResult {234return c.FindADObjects(JoinFilters(FilterIsPerson, FilterDontRequirePreauth))235}236237// GetADDomainSID returns the SID of the AD domain238// @example239// ```javascript240// const ldap = require('nuclei/ldap');241// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');242// const domainSID = client.GetADDomainSID();243// log(domainSID);244// ```245func (c *Client) GetADDomainSID() string {246r := c.Search(FilterServerTrustAccount, "objectSid")247c.nj.Require(len(r.Entries) > 0, "no result from GetADDomainSID query")248for _, entry := range r.Entries {249if sid, ok := entry.Attributes.Extra["objectSid"]; ok {250if sid, ok := sid.([]string); ok {251return DecodeSID(sid[0])252} else {253c.nj.HandleError(fmt.Errorf("invalid objectSid type: %T", entry.Attributes.Extra["objectSid"]), "invalid objectSid type")254}255}256}257c.nj.HandleError(fmt.Errorf("no objectSid found"), "no objectSid found")258return ""259}260261262