2024-01-21 16:55:17 +01:00
package ldap
import (
"fmt"
2024-01-21 17:12:09 +01:00
"strings"
2024-01-21 16:55:17 +01:00
"github.com/go-ldap/ldap/v3"
)
2024-01-21 17:12:09 +01:00
// 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 (
2024-02-07 21:45:40 +05:30
FilterIsPerson = "(objectCategory=person)" // The object is a person.
FilterIsGroup = "(objectCategory=group)" // The object is a group.
FilterIsComputer = "(objectCategory=computer)" // The object is a computer.
FilterIsAdmin = "(adminCount=1)" // The object is an admin.
FilterHasServicePrincipalName = "(servicePrincipalName=*)" // The object has a service principal name.
2024-01-21 17:12:09 +01:00
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).
)
2024-02-06 03:55:57 +05:30
// JoinFilters joins multiple filters into a single filter
2024-02-07 21:45:40 +05:30
// @example
// ```javascript
// const ldap = require('nuclei/ldap');
// const filter = ldap.JoinFilters(ldap.FilterIsPerson, ldap.FilterAccountEnabled);
// ```
2024-01-21 17:12:09 +01:00
func JoinFilters ( filters ... string ) string {
var builder strings . Builder
builder . WriteString ( "(&" )
for _ , s := range filters {
builder . WriteString ( s )
}
builder . WriteString ( ")" )
return builder . String ( )
}
2024-02-06 03:55:57 +05:30
// NegativeFilter returns a negative filter for a given filter
2024-02-07 21:45:40 +05:30
// @example
// ```javascript
// const ldap = require('nuclei/ldap');
// const filter = ldap.NegativeFilter(ldap.FilterIsPerson);
// ```
2024-01-21 17:12:09 +01:00
func NegativeFilter ( filter string ) string {
return fmt . Sprintf ( "(!%s)" , filter )
}
2024-02-07 21:45:40 +05:30
type (
// ADObject represents an Active Directory object
// @example
// ```javascript
// const ldap = require('nuclei/ldap');
// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');
// const users = client.GetADUsers();
// log(to_json(users));
2024-03-01 16:38:56 +05:30
// ```
2024-02-07 21:45:40 +05:30
ADObject struct {
DistinguishedName string
SAMAccountName string
PWDLastSet string
LastLogon string
MemberOf [ ] string
ServicePrincipalName [ ] string
}
)
2024-01-21 16:55:17 +01:00
2024-02-06 03:55:57 +05:30
// FindADObjects finds AD objects based on a filter
// and returns them as a list of ADObject
2024-02-07 21:45:40 +05:30
// @example
// ```javascript
// const ldap = require('nuclei/ldap');
// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');
// const users = client.FindADObjects(ldap.FilterIsPerson);
// log(to_json(users));
// ```
2024-02-06 03:55:57 +05:30
func ( c * Client ) FindADObjects ( filter string ) [ ] ADObject {
c . nj . Require ( c . conn != nil , "no existing connection" )
2024-01-21 16:55:17 +01:00
sr := ldap . NewSearchRequest (
c . BaseDN , ldap . ScopeWholeSubtree ,
ldap . NeverDerefAliases , 0 , 0 , false ,
filter ,
[ ] string {
"distinguishedName" ,
"sAMAccountName" ,
"pwdLastSet" ,
"lastLogon" ,
"memberOf" ,
"servicePrincipalName" ,
} ,
nil ,
)
2024-02-06 03:55:57 +05:30
res , err := c . conn . Search ( sr )
c . nj . HandleError ( err , "ldap search request failed" )
2024-01-21 16:55:17 +01:00
var objects [ ] ADObject
for _ , obj := range res . Entries {
objects = append ( objects , ADObject {
DistinguishedName : obj . GetAttributeValue ( "distinguishedName" ) ,
SAMAccountName : obj . GetAttributeValue ( "sAMAccountName" ) ,
2024-01-21 18:14:20 +01:00
PWDLastSet : DecodeADTimestamp ( obj . GetAttributeValue ( "pwdLastSet" ) ) ,
LastLogon : DecodeADTimestamp ( obj . GetAttributeValue ( "lastLogon" ) ) ,
2024-01-21 16:55:17 +01:00
MemberOf : obj . GetAttributeValues ( "memberOf" ) ,
ServicePrincipalName : obj . GetAttributeValues ( "servicePrincipalName" ) ,
} )
}
2024-02-06 03:55:57 +05:30
return objects
2024-01-21 16:55:17 +01:00
}
2024-01-21 17:11:28 +01:00
2024-02-06 03:55:57 +05:30
// GetADUsers returns all AD users
// using FilterIsPerson filter query
2024-02-07 21:45:40 +05:30
// @example
// ```javascript
// const ldap = require('nuclei/ldap');
// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');
// const users = client.GetADUsers();
// log(to_json(users));
// ```
2024-02-06 03:55:57 +05:30
func ( c * Client ) GetADUsers ( ) [ ] ADObject {
2024-01-21 17:11:28 +01:00
return c . FindADObjects ( FilterIsPerson )
}
2024-02-06 03:55:57 +05:30
// GetADActiveUsers returns all AD users
// using FilterIsPerson and FilterAccountEnabled filter query
2024-02-07 21:45:40 +05:30
// @example
// ```javascript
// const ldap = require('nuclei/ldap');
// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');
// const users = client.GetADActiveUsers();
// log(to_json(users));
// ```
2024-02-06 03:55:57 +05:30
func ( c * Client ) GetADActiveUsers ( ) [ ] ADObject {
2024-01-21 17:11:28 +01:00
return c . FindADObjects ( JoinFilters ( FilterIsPerson , FilterAccountEnabled ) )
}
2024-02-06 03:55:57 +05:30
// GetAdUserWithNeverExpiringPasswords returns all AD users
// using FilterIsPerson and FilterDontExpirePassword filter query
2024-02-07 21:45:40 +05:30
// @example
// ```javascript
// const ldap = require('nuclei/ldap');
// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');
// const users = client.GetADUserWithNeverExpiringPasswords();
// log(to_json(users));
// ```
2024-02-06 03:55:57 +05:30
func ( c * Client ) GetADUserWithNeverExpiringPasswords ( ) [ ] ADObject {
2024-01-21 17:11:28 +01:00
return c . FindADObjects ( JoinFilters ( FilterIsPerson , FilterDontExpirePassword ) )
}
2024-02-06 03:55:57 +05:30
// GetADUserTrustedForDelegation returns all AD users that are trusted for delegation
// using FilterIsPerson and FilterTrustedForDelegation filter query
2024-02-07 21:45:40 +05:30
// @example
// ```javascript
// const ldap = require('nuclei/ldap');
// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');
// const users = client.GetADUserTrustedForDelegation();
// log(to_json(users));
// ```
2024-02-06 03:55:57 +05:30
func ( c * Client ) GetADUserTrustedForDelegation ( ) [ ] ADObject {
2024-01-21 17:11:28 +01:00
return c . FindADObjects ( JoinFilters ( FilterIsPerson , FilterTrustedForDelegation ) )
}
2024-02-06 03:55:57 +05:30
// GetADUserWithPasswordNotRequired returns all AD users that do not require a password
// using FilterIsPerson and FilterPasswordNotRequired filter query
2024-02-07 21:45:40 +05:30
// @example
// ```javascript
// const ldap = require('nuclei/ldap');
// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');
// const users = client.GetADUserWithPasswordNotRequired();
// log(to_json(users));
// ```
2024-02-06 03:55:57 +05:30
func ( c * Client ) GetADUserWithPasswordNotRequired ( ) [ ] ADObject {
2024-01-21 17:11:28 +01:00
return c . FindADObjects ( JoinFilters ( FilterIsPerson , FilterPasswordNotRequired ) )
}
2024-02-06 03:55:57 +05:30
// GetADGroups returns all AD groups
// using FilterIsGroup filter query
2024-02-07 21:45:40 +05:30
// @example
// ```javascript
// const ldap = require('nuclei/ldap');
// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');
// const groups = client.GetADGroups();
// log(to_json(groups));
// ```
2024-02-06 03:55:57 +05:30
func ( c * Client ) GetADGroups ( ) [ ] ADObject {
2024-01-21 17:11:28 +01:00
return c . FindADObjects ( FilterIsGroup )
}
2024-02-06 03:55:57 +05:30
// GetADDCList returns all AD domain controllers
// using FilterIsComputer, FilterAccountEnabled and FilterServerTrustAccount filter query
2024-02-07 21:45:40 +05:30
// @example
// ```javascript
// const ldap = require('nuclei/ldap');
// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');
// const dcs = client.GetADDCList();
// log(to_json(dcs));
// ```
2024-02-06 03:55:57 +05:30
func ( c * Client ) GetADDCList ( ) [ ] ADObject {
2024-01-21 17:11:28 +01:00
return c . FindADObjects ( JoinFilters ( FilterIsComputer , FilterAccountEnabled , FilterServerTrustAccount ) )
}
2024-02-06 03:55:57 +05:30
// GetADAdmins returns all AD admins
// using FilterIsPerson, FilterAccountEnabled and FilterIsAdmin filter query
2024-02-07 21:45:40 +05:30
// @example
// ```javascript
// const ldap = require('nuclei/ldap');
// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');
// const admins = client.GetADAdmins();
// log(to_json(admins));
// ```
2024-02-06 03:55:57 +05:30
func ( c * Client ) GetADAdmins ( ) [ ] ADObject {
2024-01-21 17:11:28 +01:00
return c . FindADObjects ( JoinFilters ( FilterIsPerson , FilterAccountEnabled , FilterIsAdmin ) )
}
2024-02-06 03:55:57 +05:30
// GetADUserKerberoastable returns all AD users that are kerberoastable
// using FilterIsPerson, FilterAccountEnabled and FilterHasServicePrincipalName filter query
2024-02-07 21:45:40 +05:30
// @example
// ```javascript
// const ldap = require('nuclei/ldap');
// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');
// const kerberoastable = client.GetADUserKerberoastable();
// log(to_json(kerberoastable));
// ```
2024-02-06 03:55:57 +05:30
func ( c * Client ) GetADUserKerberoastable ( ) [ ] ADObject {
2024-01-21 17:11:28 +01:00
return c . FindADObjects ( JoinFilters ( FilterIsPerson , FilterAccountEnabled , FilterHasServicePrincipalName ) )
}
2024-01-21 17:31:08 +01:00
2024-02-06 03:55:57 +05:30
// GetADDomainSID returns the SID of the AD domain
2024-02-07 21:45:40 +05:30
// @example
// ```javascript
// const ldap = require('nuclei/ldap');
// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');
// const domainSID = client.GetADDomainSID();
// log(domainSID);
// ```
2024-02-06 03:55:57 +05:30
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 ] )
2024-01-21 17:31:08 +01:00
}