diff --git a/pkg/js/compiler/pool.go b/pkg/js/compiler/pool.go index 17d9f746f5..3407d97369 100644 --- a/pkg/js/compiler/pool.go +++ b/pkg/js/compiler/pool.go @@ -241,5 +241,5 @@ func stringify(gojaValue goja.Value, runtime *goja.Runtime) string { } } // for everything else stringify - return fmt.Sprintf("%v", value) + return fmt.Sprintf("%+v", value) } diff --git a/pkg/js/generated/go/libldap/ldap.go b/pkg/js/generated/go/libldap/ldap.go index 8d0c2702e4..978ded0c09 100644 --- a/pkg/js/generated/go/libldap/ldap.go +++ b/pkg/js/generated/go/libldap/ldap.go @@ -53,10 +53,12 @@ func init() { "FilterWorkstationTrustAccount": lib_ldap.FilterWorkstationTrustAccount, // Objects / Classes - "ADObject": gojs.GetClassConstructor[lib_ldap.ADObject](&lib_ldap.ADObject{}), - "Client": lib_ldap.NewClient, - "Config": gojs.GetClassConstructor[lib_ldap.Config](&lib_ldap.Config{}), - "Metadata": gojs.GetClassConstructor[lib_ldap.Metadata](&lib_ldap.Metadata{}), + "Client": lib_ldap.NewClient, + "Config": gojs.GetClassConstructor[lib_ldap.Config](&lib_ldap.Config{}), + "LdapAttributes": gojs.GetClassConstructor[lib_ldap.LdapAttributes](&lib_ldap.LdapAttributes{}), + "LdapEntry": gojs.GetClassConstructor[lib_ldap.LdapEntry](&lib_ldap.LdapEntry{}), + "Metadata": gojs.GetClassConstructor[lib_ldap.Metadata](&lib_ldap.Metadata{}), + "SearchResult": gojs.GetClassConstructor[lib_ldap.SearchResult](&lib_ldap.SearchResult{}), }, ).Register() } diff --git a/pkg/js/generated/ts/kerberos.ts b/pkg/js/generated/ts/kerberos.ts index 8f9ce98f99..d0af2d0e7e 100755 --- a/pkg/js/generated/ts/kerberos.ts +++ b/pkg/js/generated/ts/kerberos.ts @@ -188,9 +188,9 @@ export class Config { */ export interface AuthorizationDataEntry { - ADType?: number, - ADData?: Uint8Array, + + ADType?: number, } @@ -200,9 +200,9 @@ export interface AuthorizationDataEntry { */ export interface BitString { - BitLength?: number, - Bytes?: Uint8Array, + + BitLength?: number, } @@ -212,9 +212,9 @@ export interface BitString { */ export interface BitString { - BitLength?: number, - Bytes?: Uint8Array, + + BitLength?: number, } @@ -236,19 +236,15 @@ export interface Config { */ export interface EncTicketPart { - AuthTime?: Date, - - StartTime?: Date, - EndTime?: Date, RenewTill?: Date, CRealm?: string, - CAddr?: HostAddress, + AuthTime?: Date, - AuthorizationData?: AuthorizationDataEntry, + StartTime?: Date, Flags?: BitString, @@ -257,6 +253,10 @@ export interface EncTicketPart { CName?: PrincipalName, Transited?: TransitedEncoding, + + CAddr?: HostAddress, + + AuthorizationData?: AuthorizationDataEntry, } @@ -266,11 +266,11 @@ export interface EncTicketPart { */ export interface EncryptedData { + EType?: number, + KVNO?: number, Cipher?: Uint8Array, - - EType?: number, } @@ -318,41 +318,33 @@ export interface HostAddress { */ export interface LibDefaults { - NoAddresses?: boolean, + CCacheType?: number, - RealmTryDomains?: number, + K5LoginAuthoritative?: boolean, - DNSLookupKDC?: boolean, + Proxiable?: boolean, - DefaultRealm?: string, + RDNS?: boolean, - SafeChecksumType?: number, + K5LoginDirectory?: string, - VerifyAPReqNofail?: boolean, + KDCTimeSync?: number, - AllowWeakCrypto?: boolean, + VerifyAPReqNofail?: boolean, DefaultTGSEnctypes?: string[], - DefaultTktEnctypeIDs?: number[], - - IgnoreAcceptorHostname?: boolean, + DefaultTGSEnctypeIDs?: number[], - K5LoginAuthoritative?: boolean, + DNSCanonicalizeHostname?: boolean, - PermittedEnctypes?: string[], + Forwardable?: boolean, /** * time in nanoseconds */ - Clockskew?: number, - - DNSCanonicalizeHostname?: boolean, - - Proxiable?: boolean, - - RDNS?: boolean, + RenewLifetime?: number, /** * time in nanoseconds @@ -362,37 +354,45 @@ export interface LibDefaults { DefaultClientKeytabName?: string, + DefaultTktEnctypeIDs?: number[], + + DNSLookupRealm?: boolean, + + ExtraAddresses?: Uint8Array, + + DefaultRealm?: string, + + NoAddresses?: boolean, + + PreferredPreauthTypes?: number[], + PermittedEnctypeIDs?: number[], - UDPPreferenceLimit?: number, + RealmTryDomains?: number, - DefaultTGSEnctypeIDs?: number[], + DefaultKeytabName?: string, DefaultTktEnctypes?: string[], - CCacheType?: number, - - DNSLookupRealm?: boolean, + DNSLookupKDC?: boolean, - ExtraAddresses?: Uint8Array, + IgnoreAcceptorHostname?: boolean, - PreferredPreauthTypes?: number[], + AllowWeakCrypto?: boolean, Canonicalize?: boolean, - Forwardable?: boolean, - - K5LoginDirectory?: string, + SafeChecksumType?: number, - KDCTimeSync?: number, + UDPPreferenceLimit?: number, /** * time in nanoseconds */ - RenewLifetime?: number, + Clockskew?: number, - DefaultKeytabName?: string, + PermittedEnctypes?: string[], KDCDefaultOptions?: BitString, } @@ -416,6 +416,8 @@ export interface PrincipalName { */ export interface Realm { + Realm?: string, + AdminServer?: string[], DefaultDomain?: string, @@ -425,8 +427,6 @@ export interface Realm { KPasswdServer?: string[], MasterKDC?: string[], - - Realm?: string, } diff --git a/pkg/js/generated/ts/ldap.ts b/pkg/js/generated/ts/ldap.ts index daca4f86b9..02a10075b9 100755 --- a/pkg/js/generated/ts/ldap.ts +++ b/pkg/js/generated/ts/ldap.ts @@ -209,8 +209,8 @@ export class Client { * log(to_json(users)); * ``` */ - public FindADObjects(filter: string): ADObject[] { - return []; + public FindADObjects(filter: string): SearchResult | null { + return null; } @@ -225,8 +225,8 @@ export class Client { * log(to_json(users)); * ``` */ - public GetADUsers(): ADObject[] { - return []; + public GetADUsers(): SearchResult | null { + return null; } @@ -241,8 +241,8 @@ export class Client { * log(to_json(users)); * ``` */ - public GetADActiveUsers(): ADObject[] { - return []; + public GetADActiveUsers(): SearchResult | null { + return null; } @@ -257,8 +257,8 @@ export class Client { * log(to_json(users)); * ``` */ - public GetADUserWithNeverExpiringPasswords(): ADObject[] { - return []; + public GetADUserWithNeverExpiringPasswords(): SearchResult | null { + return null; } @@ -273,8 +273,8 @@ export class Client { * log(to_json(users)); * ``` */ - public GetADUserTrustedForDelegation(): ADObject[] { - return []; + public GetADUserTrustedForDelegation(): SearchResult | null { + return null; } @@ -289,8 +289,8 @@ export class Client { * log(to_json(users)); * ``` */ - public GetADUserWithPasswordNotRequired(): ADObject[] { - return []; + public GetADUserWithPasswordNotRequired(): SearchResult | null { + return null; } @@ -305,8 +305,8 @@ export class Client { * log(to_json(groups)); * ``` */ - public GetADGroups(): ADObject[] { - return []; + public GetADGroups(): SearchResult | null { + return null; } @@ -321,8 +321,8 @@ export class Client { * log(to_json(dcs)); * ``` */ - public GetADDCList(): ADObject[] { - return []; + public GetADDCList(): SearchResult | null { + return null; } @@ -337,8 +337,8 @@ export class Client { * log(to_json(admins)); * ``` */ - public GetADAdmins(): ADObject[] { - return []; + public GetADAdmins(): SearchResult | null { + return null; } @@ -353,8 +353,8 @@ export class Client { * log(to_json(kerberoastable)); * ``` */ - public GetADUserKerberoastable(): ADObject[] { - return []; + public GetADUserKerberoastable(): SearchResult | null { + return null; } @@ -369,8 +369,8 @@ export class Client { * log(to_json(AsRepRoastable)); * ``` */ - public GetADUserAsRepRoastable(): ADObject[] { - return []; + public GetADUserAsRepRoastable(): SearchResult | null { + return null; } @@ -428,8 +428,8 @@ export class Client { * const results = client.Search('(objectClass=*)', 'cn', 'mail'); * ``` */ - public Search(filter: string, attributes: any): Record[] { - return []; + public Search(filter: string, attributes: any): SearchResult | null { + return null; } @@ -481,33 +481,6 @@ export class Client { -/** - * 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)); - * ``` - */ -export interface ADObject { - - DistinguishedName?: string, - - SAMAccountName?: string, - - PWDLastSet?: string, - - LastLogon?: string, - - MemberOf?: string[], - - ServicePrincipalName?: string[], -} - - - /** * Config is extra configuration for the ldap client * @example @@ -535,27 +508,186 @@ export interface Config { /** - * Entry Interface + * LdapAttributes represents all LDAP attributes of a particular + * ldap entry */ -export interface Entry { +export interface LdapAttributes { - DN?: string, + /** + * CurrentTime contains current time + */ + + CurrentTime?: string[], + + /** + * SubschemaSubentry contains subschema subentry + */ + + SubschemaSubentry?: string[], + + /** + * DsServiceName contains ds service name + */ + + DsServiceName?: string[], + + /** + * NamingContexts contains naming contexts + */ + + NamingContexts?: string[], + + /** + * DefaultNamingContext contains default naming context + */ + + DefaultNamingContext?: string[], + + /** + * SchemaNamingContext contains schema naming context + */ + + SchemaNamingContext?: string[], + + /** + * ConfigurationNamingContext contains configuration naming context + */ + + ConfigurationNamingContext?: string[], + + /** + * RootDomainNamingContext contains root domain naming context + */ + + RootDomainNamingContext?: string[], + + /** + * SupportedLDAPVersion contains supported LDAP version + */ + + SupportedLDAPVersion?: string[], + + /** + * HighestCommittedUSN contains highest committed USN + */ + + HighestCommittedUSN?: string[], + + /** + * SupportedSASLMechanisms contains supported SASL mechanisms + */ + + SupportedSASLMechanisms?: string[], + + /** + * DnsHostName contains DNS host name + */ + + DnsHostName?: string[], - Attributes?: EntryAttribute, + /** + * LdapServiceName contains LDAP service name + */ + + LdapServiceName?: string[], + + /** + * ServerName contains server name + */ + + ServerName?: string[], + + /** + * IsSynchronized contains is synchronized + */ + + IsSynchronized?: string[], + + /** + * IsGlobalCatalogReady contains is global catalog ready + */ + + IsGlobalCatalogReady?: string[], + + /** + * DomainFunctionality contains domain functionality + */ + + DomainFunctionality?: string[], + + /** + * ForestFunctionality contains forest functionality + */ + + ForestFunctionality?: string[], + + /** + * DomainControllerFunctionality contains domain controller functionality + */ + + DomainControllerFunctionality?: string[], + + /** + * DistinguishedName contains the distinguished name + */ + + DistinguishedName?: string[], + + /** + * SAMAccountName contains the SAM account name + */ + + SAMAccountName?: string[], + + /** + * PWDLastSet contains the password last set time + */ + + PWDLastSet?: string[], + + /** + * LastLogon contains the last logon time + */ + + LastLogon?: string[], + + /** + * MemberOf contains the groups the entry is a member of + */ + + MemberOf?: string[], + + /** + * ServicePrincipalName contains the service principal names + */ + + ServicePrincipalName?: string[], + + /** + * Extra contains other extra fields which might be present + */ + + Extra?: Record, } /** - * EntryAttribute Interface + * LdapEntry represents a single LDAP entry */ -export interface EntryAttribute { +export interface LdapEntry { - Name?: string, + /** + * DN contains distinguished name + */ - Values?: string[], + DN?: string, - ByteValues?: Uint8Array, + /** + * Attributes contains list of attributes + */ + + Attributes?: LdapAttributes, } @@ -584,12 +716,32 @@ export interface Metadata { /** - * SearchResult Interface + * SearchResult contains search result of any / all ldap search request + * @example + * ```javascript + * const ldap = require('nuclei/ldap'); + * const client = new ldap.Client('ldap://ldap.example.com', 'acme.com'); + * const results = client.Search('(objectinterface=*)', 'cn', 'mail'); + * ``` */ export interface SearchResult { + /** + * Referrals contains list of referrals + */ + Referrals?: string[], - Entries?: Entry, + /** + * Controls contains list of controls + */ + + Controls?: string[], + + /** + * Entries contains list of entries + */ + + Entries?: LdapEntry[], } diff --git a/pkg/js/generated/ts/rdp.ts b/pkg/js/generated/ts/rdp.ts index 77dfeafb13..7858b78319 100755 --- a/pkg/js/generated/ts/rdp.ts +++ b/pkg/js/generated/ts/rdp.ts @@ -78,8 +78,6 @@ export interface IsRDPResponse { */ export interface ServiceRDP { - DNSDomainName?: string, - ForestName?: string, OSFingerprint?: string, @@ -93,5 +91,7 @@ export interface ServiceRDP { NetBIOSDomainName?: string, DNSComputerName?: string, + + DNSDomainName?: string, } diff --git a/pkg/js/generated/ts/smb.ts b/pkg/js/generated/ts/smb.ts index b0ca997ac5..c9b769cb5d 100755 --- a/pkg/js/generated/ts/smb.ts +++ b/pkg/js/generated/ts/smb.ts @@ -137,6 +137,10 @@ export interface NegotiationLog { */ export interface SMBCapabilities { + DFSSupport?: boolean, + + Leasing?: boolean, + LargeMTU?: boolean, MultiChan?: boolean, @@ -146,10 +150,6 @@ export interface SMBCapabilities { DirLeasing?: boolean, Encryption?: boolean, - - DFSSupport?: boolean, - - Leasing?: boolean, } @@ -159,16 +159,16 @@ export interface SMBCapabilities { */ export interface SMBLog { - SupportV1?: boolean, - - NativeOs?: string, - NTLM?: string, GroupName?: string, HasNTLM?: boolean, + SupportV1?: boolean, + + NativeOs?: string, + Version?: SMBVersions, Capabilities?: SMBCapabilities, @@ -185,13 +185,13 @@ export interface SMBLog { */ export interface SMBVersions { - VerString?: string, - Major?: number, Minor?: number, Revision?: number, + + VerString?: string, } @@ -225,12 +225,12 @@ export interface ServiceSMB { */ export interface SessionSetupLog { - NegotiateFlags?: number, - SetupFlags?: number, TargetName?: string, + NegotiateFlags?: number, + HeaderLog?: HeaderLog, } diff --git a/pkg/js/generated/ts/ssh.ts b/pkg/js/generated/ts/ssh.ts index 21dacf4054..058df03c5b 100755 --- a/pkg/js/generated/ts/ssh.ts +++ b/pkg/js/generated/ts/ssh.ts @@ -133,9 +133,9 @@ export interface Algorithms { HostKey?: string, - R?: DirectionAlgorithms, - W?: DirectionAlgorithms, + + R?: DirectionAlgorithms, } @@ -159,13 +159,13 @@ export interface DirectionAlgorithms { */ export interface EndpointId { - Raw?: string, - - ProtoVersion?: string, - SoftwareVersion?: string, Comment?: string, + + Raw?: string, + + ProtoVersion?: string, } @@ -197,34 +197,34 @@ export interface HandshakeLog { */ export interface KexInitMsg { - Reserved?: number, - - /** - * fixed size array of length: [16] - */ - - Cookie?: Uint8Array, + KexAlgos?: string[], CiphersClientServer?: string[], - MACsClientServer?: string[], - MACsServerClient?: string[], + LanguagesClientServer?: string[], + + CompressionClientServer?: string[], + CompressionServerClient?: string[], - LanguagesClientServer?: string[], + Reserved?: number, - FirstKexFollows?: boolean, + MACsClientServer?: string[], - KexAlgos?: string[], + /** + * fixed size array of length: [16] + */ - CiphersServerClient?: string[], + Cookie?: Uint8Array, - CompressionClientServer?: string[], + ServerHostKeyAlgos?: string[], + + CiphersServerClient?: string[], LanguagesServerClient?: string[], - ServerHostKeyAlgos?: string[], + FirstKexFollows?: boolean, } diff --git a/pkg/js/libs/ldap/adenum.go b/pkg/js/libs/ldap/adenum.go index 9aea98be97..849e4d6a15 100644 --- a/pkg/js/libs/ldap/adenum.go +++ b/pkg/js/libs/ldap/adenum.go @@ -74,25 +74,6 @@ func NegativeFilter(filter string) string { return fmt.Sprintf("(!%s)", filter) } -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)); - // ``` - 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 // @example @@ -102,7 +83,7 @@ type ( // const users = client.FindADObjects(ldap.FilterIsPerson); // log(to_json(users)); // ``` -func (c *Client) FindADObjects(filter string) []ADObject { +func (c *Client) FindADObjects(filter string) SearchResult { c.nj.Require(c.conn != nil, "no existing connection") sr := ldap.NewSearchRequest( c.BaseDN, ldap.ScopeWholeSubtree, @@ -121,19 +102,7 @@ func (c *Client) FindADObjects(filter string) []ADObject { 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 + return *getSearchResult(res) } // GetADUsers returns all AD users @@ -145,7 +114,7 @@ func (c *Client) FindADObjects(filter string) []ADObject { // const users = client.GetADUsers(); // log(to_json(users)); // ``` -func (c *Client) GetADUsers() []ADObject { +func (c *Client) GetADUsers() SearchResult { return c.FindADObjects(FilterIsPerson) } @@ -158,7 +127,7 @@ func (c *Client) GetADUsers() []ADObject { // const users = client.GetADActiveUsers(); // log(to_json(users)); // ``` -func (c *Client) GetADActiveUsers() []ADObject { +func (c *Client) GetADActiveUsers() SearchResult { return c.FindADObjects(JoinFilters(FilterIsPerson, FilterAccountEnabled)) } @@ -171,7 +140,7 @@ func (c *Client) GetADActiveUsers() []ADObject { // const users = client.GetADUserWithNeverExpiringPasswords(); // log(to_json(users)); // ``` -func (c *Client) GetADUserWithNeverExpiringPasswords() []ADObject { +func (c *Client) GetADUserWithNeverExpiringPasswords() SearchResult { return c.FindADObjects(JoinFilters(FilterIsPerson, FilterDontExpirePassword)) } @@ -184,7 +153,7 @@ func (c *Client) GetADUserWithNeverExpiringPasswords() []ADObject { // const users = client.GetADUserTrustedForDelegation(); // log(to_json(users)); // ``` -func (c *Client) GetADUserTrustedForDelegation() []ADObject { +func (c *Client) GetADUserTrustedForDelegation() SearchResult { return c.FindADObjects(JoinFilters(FilterIsPerson, FilterTrustedForDelegation)) } @@ -197,7 +166,7 @@ func (c *Client) GetADUserTrustedForDelegation() []ADObject { // const users = client.GetADUserWithPasswordNotRequired(); // log(to_json(users)); // ``` -func (c *Client) GetADUserWithPasswordNotRequired() []ADObject { +func (c *Client) GetADUserWithPasswordNotRequired() SearchResult { return c.FindADObjects(JoinFilters(FilterIsPerson, FilterPasswordNotRequired)) } @@ -210,7 +179,7 @@ func (c *Client) GetADUserWithPasswordNotRequired() []ADObject { // const groups = client.GetADGroups(); // log(to_json(groups)); // ``` -func (c *Client) GetADGroups() []ADObject { +func (c *Client) GetADGroups() SearchResult { return c.FindADObjects(FilterIsGroup) } @@ -223,7 +192,7 @@ func (c *Client) GetADGroups() []ADObject { // const dcs = client.GetADDCList(); // log(to_json(dcs)); // ``` -func (c *Client) GetADDCList() []ADObject { +func (c *Client) GetADDCList() SearchResult { return c.FindADObjects(JoinFilters(FilterIsComputer, FilterAccountEnabled, FilterServerTrustAccount)) } @@ -236,7 +205,7 @@ func (c *Client) GetADDCList() []ADObject { // const admins = client.GetADAdmins(); // log(to_json(admins)); // ``` -func (c *Client) GetADAdmins() []ADObject { +func (c *Client) GetADAdmins() SearchResult { return c.FindADObjects(JoinFilters(FilterIsPerson, FilterAccountEnabled, FilterIsAdmin)) } @@ -249,7 +218,7 @@ func (c *Client) GetADAdmins() []ADObject { // const kerberoastable = client.GetADUserKerberoastable(); // log(to_json(kerberoastable)); // ``` -func (c *Client) GetADUserKerberoastable() []ADObject { +func (c *Client) GetADUserKerberoastable() SearchResult { return c.FindADObjects(JoinFilters(FilterIsPerson, FilterAccountEnabled, FilterHasServicePrincipalName)) } @@ -262,7 +231,7 @@ func (c *Client) GetADUserKerberoastable() []ADObject { // const AsRepRoastable = client.GetADUserAsRepRoastable(); // log(to_json(AsRepRoastable)); // ``` -func (c *Client) GetADUserAsRepRoastable() []ADObject { +func (c *Client) GetADUserAsRepRoastable() SearchResult { return c.FindADObjects(JoinFilters(FilterIsPerson, FilterDontRequirePreauth)) } @@ -276,7 +245,16 @@ func (c *Client) GetADUserAsRepRoastable() []ADObject { // ``` 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]) + c.nj.Require(len(r.Entries) > 0, "no result from GetADDomainSID query") + for _, entry := range r.Entries { + if sid, ok := entry.Attributes.Extra["objectSid"]; ok { + if sid, ok := sid.([]string); ok { + return DecodeSID(sid[0]) + } else { + c.nj.HandleError(fmt.Errorf("invalid objectSid type: %T", entry.Attributes.Extra["objectSid"]), "invalid objectSid type") + } + } + } + c.nj.HandleError(fmt.Errorf("no objectSid found"), "no objectSid found") + return "" } diff --git a/pkg/js/libs/ldap/ldap.go b/pkg/js/libs/ldap/ldap.go index 31afa232d0..3961fa7b7e 100644 --- a/pkg/js/libs/ldap/ldap.go +++ b/pkg/js/libs/ldap/ldap.go @@ -203,7 +203,7 @@ func (c *Client) AuthenticateWithNTLMHash(username, hash string) { // const client = new ldap.Client('ldap://ldap.example.com', 'acme.com'); // const results = client.Search('(objectClass=*)', 'cn', 'mail'); // ``` -func (c *Client) Search(filter string, attributes ...string) []map[string][]string { +func (c *Client) Search(filter string, attributes ...string) SearchResult { c.nj.Require(c.conn != nil, "no existing connection") c.nj.Require(c.BaseDN != "", "base dn cannot be empty") c.nj.Require(len(attributes) > 0, "attributes cannot be empty") @@ -211,7 +211,7 @@ func (c *Client) Search(filter string, attributes ...string) []map[string][]stri res, err := c.conn.Search( ldap.NewSearchRequest( "", - ldap.ScopeBaseObject, + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, filter, @@ -220,28 +220,7 @@ func (c *Client) Search(filter string, attributes ...string) []map[string][]stri ), ) c.nj.HandleError(err, "ldap search request failed") - if len(res.Entries) == 0 { - // return empty list - return nil - } - - // convert ldap.Entry to []map[string][]string - var out []map[string][]string - for _, r := range res.Entries { - app := make(map[string][]string) - empty := true - for _, a := range attributes { - v := r.GetAttributeValues(a) - if len(v) > 0 { - app[a] = v - empty = false - } - } - if !empty { - out = append(out, app) - } - } - return out + return *getSearchResult(res) } // AdvancedSearch accepts all values of search request type and return Ldap Entry @@ -257,7 +236,7 @@ func (c *Client) AdvancedSearch( TypesOnly bool, Filter string, Attributes []string, - Controls []ldap.Control) ldap.SearchResult { + Controls []ldap.Control) SearchResult { c.nj.Require(c.conn != nil, "no existing connection") if c.BaseDN == "" { c.BaseDN = fmt.Sprintf("dc=%s", strings.Join(strings.Split(c.Realm, "."), ",dc=")) @@ -266,7 +245,7 @@ func (c *Client) AdvancedSearch( res, err := c.conn.Search(req) c.nj.HandleError(err, "ldap search request failed") c.nj.Require(res != nil, "ldap search request failed got nil response") - return *res + return *getSearchResult(res) } type ( @@ -302,7 +281,7 @@ func (c *Client) CollectMetadata() Metadata { srMetadata := ldap.NewSearchRequest( "", - ldap.ScopeBaseObject, + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, "(objectClass=*)", diff --git a/pkg/js/libs/ldap/utils.go b/pkg/js/libs/ldap/utils.go index 96a243bbc7..bcff41dc49 100644 --- a/pkg/js/libs/ldap/utils.go +++ b/pkg/js/libs/ldap/utils.go @@ -5,8 +5,189 @@ import ( "strconv" "strings" "time" + + "github.com/go-ldap/ldap/v3" ) +type ( + // SearchResult contains search result of any / all ldap search request + // @example + // ```javascript + // const ldap = require('nuclei/ldap'); + // const client = new ldap.Client('ldap://ldap.example.com', 'acme.com'); + // const results = client.Search('(objectClass=*)', 'cn', 'mail'); + // ``` + SearchResult struct { + // Referrals contains list of referrals + Referrals []string `json:"referrals"` + // Controls contains list of controls + Controls []string `json:"controls"` + // Entries contains list of entries + Entries []LdapEntry `json:"entries"` + } + + // LdapEntry represents a single LDAP entry + LdapEntry struct { + // DN contains distinguished name + DN string `json:"dn"` + // Attributes contains list of attributes + Attributes LdapAttributes `json:"attributes"` + } + + // LdapAttributes represents all LDAP attributes of a particular + // ldap entry + LdapAttributes struct { + // CurrentTime contains current time + CurrentTime []string `json:"currentTime,omitempty"` + // SubschemaSubentry contains subschema subentry + SubschemaSubentry []string `json:"subschemaSubentry,omitempty"` + // DsServiceName contains ds service name + DsServiceName []string `json:"dsServiceName,omitempty"` + // NamingContexts contains naming contexts + NamingContexts []string `json:"namingContexts,omitempty"` + // DefaultNamingContext contains default naming context + DefaultNamingContext []string `json:"defaultNamingContext,omitempty"` + // SchemaNamingContext contains schema naming context + SchemaNamingContext []string `json:"schemaNamingContext,omitempty"` + // ConfigurationNamingContext contains configuration naming context + ConfigurationNamingContext []string `json:"configurationNamingContext,omitempty"` + // RootDomainNamingContext contains root domain naming context + RootDomainNamingContext []string `json:"rootDomainNamingContext,omitempty"` + // SupportedLDAPVersion contains supported LDAP version + SupportedLDAPVersion []string `json:"supportedLDAPVersion,omitempty"` + // HighestCommittedUSN contains highest committed USN + HighestCommittedUSN []string `json:"highestCommittedUSN,omitempty"` + // SupportedSASLMechanisms contains supported SASL mechanisms + SupportedSASLMechanisms []string `json:"supportedSASLMechanisms,omitempty"` + // DnsHostName contains DNS host name + DnsHostName []string `json:"dnsHostName,omitempty"` + // LdapServiceName contains LDAP service name + LdapServiceName []string `json:"ldapServiceName,omitempty"` + // ServerName contains server name + ServerName []string `json:"serverName,omitempty"` + // IsSynchronized contains is synchronized + IsSynchronized []string `json:"isSynchronized,omitempty"` + // IsGlobalCatalogReady contains is global catalog ready + IsGlobalCatalogReady []string `json:"isGlobalCatalogReady,omitempty"` + // DomainFunctionality contains domain functionality + DomainFunctionality []string `json:"domainFunctionality,omitempty"` + // ForestFunctionality contains forest functionality + ForestFunctionality []string `json:"forestFunctionality,omitempty"` + // DomainControllerFunctionality contains domain controller functionality + DomainControllerFunctionality []string `json:"domainControllerFunctionality,omitempty"` + // DistinguishedName contains the distinguished name + DistinguishedName []string `json:"distinguishedName,omitempty"` + // SAMAccountName contains the SAM account name + SAMAccountName []string `json:"sAMAccountName,omitempty"` + // PWDLastSet contains the password last set time + PWDLastSet []string `json:"pwdLastSet,omitempty"` + // LastLogon contains the last logon time + LastLogon []string `json:"lastLogon,omitempty"` + // MemberOf contains the groups the entry is a member of + MemberOf []string `json:"memberOf,omitempty"` + // ServicePrincipalName contains the service principal names + ServicePrincipalName []string `json:"servicePrincipalName,omitempty"` + // Extra contains other extra fields which might be present + Extra map[string]any `json:"extra,omitempty"` + } +) + +// getSearchResult converts a ldap.SearchResult to a SearchResult +func getSearchResult(sr *ldap.SearchResult) *SearchResult { + t := &SearchResult{ + Referrals: []string{}, + Controls: []string{}, + Entries: []LdapEntry{}, + } + // add referrals + t.Referrals = append(t.Referrals, sr.Referrals...) + // add controls + for _, ctrl := range sr.Controls { + t.Controls = append(t.Controls, ctrl.String()) + } + // add entries + for _, entry := range sr.Entries { + t.Entries = append(t.Entries, parseLdapEntry(entry)) + } + return t +} + +func parseLdapEntry(entry *ldap.Entry) LdapEntry { + e := LdapEntry{ + DN: entry.DN, + } + attrs := LdapAttributes{ + Extra: make(map[string]any), + } + for _, attr := range entry.Attributes { + switch attr.Name { + case "currentTime": + attrs.CurrentTime = decodeTimestamps(attr.Values) + case "subschemaSubentry": + attrs.SubschemaSubentry = attr.Values + case "dsServiceName": + attrs.DsServiceName = attr.Values + case "namingContexts": + attrs.NamingContexts = attr.Values + case "defaultNamingContext": + attrs.DefaultNamingContext = attr.Values + case "schemaNamingContext": + attrs.SchemaNamingContext = attr.Values + case "configurationNamingContext": + attrs.ConfigurationNamingContext = attr.Values + case "rootDomainNamingContext": + attrs.RootDomainNamingContext = attr.Values + case "supportedLDAPVersion": + attrs.SupportedLDAPVersion = attr.Values + case "highestCommittedUSN": + attrs.HighestCommittedUSN = attr.Values + case "supportedSASLMechanisms": + attrs.SupportedSASLMechanisms = attr.Values + case "dnsHostName": + attrs.DnsHostName = attr.Values + case "ldapServiceName": + attrs.LdapServiceName = attr.Values + case "serverName": + attrs.ServerName = attr.Values + case "isSynchronized": + attrs.IsSynchronized = attr.Values + case "isGlobalCatalogReady": + attrs.IsGlobalCatalogReady = attr.Values + case "domainFunctionality": + attrs.DomainFunctionality = attr.Values + case "forestFunctionality": + attrs.ForestFunctionality = attr.Values + case "domainControllerFunctionality": + attrs.DomainControllerFunctionality = attr.Values + case "distinguishedName": + attrs.DistinguishedName = attr.Values + case "sAMAccountName": + attrs.SAMAccountName = attr.Values + case "pwdLastSet": + attrs.PWDLastSet = decodeTimestamps(attr.Values) + case "lastLogon": + attrs.LastLogon = decodeTimestamps(attr.Values) + case "memberOf": + attrs.MemberOf = attr.Values + case "servicePrincipalName": + attrs.ServicePrincipalName = attr.Values + default: + attrs.Extra[attr.Name] = attr.Values + } + } + e.Attributes = attrs + return e +} + +// decodeTimestamps decodes multiple timestamps +func decodeTimestamps(timestamps []string) []string { + res := []string{} + for _, timestamp := range timestamps { + res = append(res, DecodeADTimestamp(timestamp)) + } + return res +} + // DecodeSID decodes a SID string // @example // ```javascript