Active Directory by Microsoft

Network discovery

An Active Directory (“AD”) is a technology from Microsoft which aims to ease the management of organizations through a centralized authentication (“SSO” for Single Sign-On), an inventory of users and machines, a way to apply configurations, etc. This uses several protocols:

  • Kerberos (RFC 4120, TCP port 88) for authentication ;

  • LDAP (Lightweight Directory Access Protocol, RFC 4511, TCP ports 389 for LDAP and 636 for LDAPS) to access properties of the directory (user names, group memberships, etc.) ;

  • DNS (Domain Name System, UDP and TCP port 53) to allow clients to discover the servers which host the AD, the Domain Controllers (DC) ;

  • MS-RPC and SMB (Server Message Block Protocol, TCP port 445) to share configuration files and programs (such as Group Policy Objects, GPO) with clients ;

  • etc.

On a network which uses an AD, the machines are usually configured such that their DNS resolver is a domain controller and the DNS search suffix is the name of the AD domain. Such a configuration can be easily discovered, for example through DHCP.

A way to discover the Domain Controllers of a specific domain, such as contoso.com (the example company of Microsoft, cf. https://en.wikipedia.org/wiki/Contoso) consists in issuing DNS requests with type SRV to:

  • _kerberos._tcp.contoso.com (enumerate the servers which provide the Kerberos protocol, TCP and UDP 88)

  • _kerberos._udp.contoso.com

  • _kpasswd._tcp.contoso.com (for Kerberos password change servers, TCP and UDP 464)

  • _kpasswd._udp.contoso.com

  • _ldap._tcp.contoso.com (for LDAP, TCP 389)

  • _ldaps._tcp.contoso.com (for LDAPS, TCP 636)

  • _gc._tcp.contoso.com (for Global Catalog, LDAP for the entire forest, TCP 3268 for LDAP and 3269 for LDAPS)

A DNS response to a SRV request contains a priority number (0), a weight number (100), a TCP (or UDP) port number and a host name that can be resolved to an IPv4 or an IPv6 (type A or AAAA records).

There also exists a specific DNS zone for “Microsoft Domain Controller Server”, _msdcs.contoso.com:

  • _ldap._tcp.dc._msdcs.contoso.com (“DC” means “Domain Controller”)

  • _ldap._tcp.gc._msdcs.contoso.com (“GC” means “Global Catalog”)

  • _ldap._tcp.pdc._msdcs.contoso.com (“PDC” means “Primary Domain Controller”, which is usually unique per domain)

  • _kerberos._tcp.dc._msdcs.contoso.com

  • A and AAAA requests to gc._msdcs.contoso.com also resolves to IP addresses of DC.

Kerberos configuration

In order to authenticate to an AD using an account through Kerberos protocol, kinit can be used (from package krb5). Its configuration lies in /etc/krb5.conf and looks like:

[libdefaults]
    default_realm = CONTOSO.COM
    dns_lookup_realm = false
    dns_lookup_kdc = true
    forwardable = true

[realms]
# use "kdc = ..." if realm admins haven't put SRV records into DNS
    ATHENA.MIT.EDU = {
        admin_server = kerberos.mit.edu
    }
    CONTOSO.COM = {
        kdc = 192.168.0.1:88
        kdc_tcp_ports = 88
        admin_server = 192.168.0.1
        default_domain = CONTOSO.COM

        # For smartcard authentication
        pkinit_anchors = FILE:/etc/ssl/contoso.com/ca.crt
        pkinit_kdc_hostname = dc1.contoso.com
        pkinit_kdc_hostname = dc2.contoso.com
        pkinit_cert_match = <SUBJECT>CN=mylogin.*
        pkinit_identities = PKCS11:/usr/lib/pkcs11/opensc-pkcs11.so
    }

[domain_realm]
    .contoso.com = CONTOSO.COM
    contoso.com = CONTOSO.COM

[logging]
#    kdc = CONSOLE
#    default = FILE:/tmp/krb5log.log
#    kdc = FILE:/tmp/krb5log.log
#    admin_server = FILE:/tmp/krb5admin.log

kinit stores Kerberos tickets in /tmp/krb5cc_${UID} where UID is the user identifier. This can be overridden using variable $KRB5CCNAME. The tickets can then be used for example with:

# From samba
smbclient -k //machine.contoso.com/IPC$
rpclient -k machine.contoso.com
# ... and issue "getusername"

# From impacket
wmiexec.py -k -no-pass contoso.com/mylogin@machine.contoso.com

LDAP queries

In order to query an AD using ldapsearch, several parameters need to be found:

  • The connection string (protocol, host name, TCP port) to the LDAP or LDAPS server, like ldaps://dc1.contoso.com.

  • A user account, with a login and the associated password, like mylogin and P@ssw0rd!.

  • The base DN (Distinguished Name) of the directory, which can be derived from the domain name of the AD using DC= components, like DC=contoso,DC=com.

    • DC means Domain Component (for the domain name)

    • OU means Organizational Unit (like folders)

    • CN means Common Name (designation of objects)

With all these parameters, a LDAP search command looks like:

# -N to not use reverse DNS to canonicalize the SASL host name
# -H connection string (LDAP URI)
# -D login ("bind DN") and -w password, or -W to prompt for the password
#    or -y to specify a password file
#    or -Y GSSAPI to use Kerberos (with SASL, requires package cyrus-sasl-gssapi)
# -x to use simple authentication instead of SASL
# -LLL to disable printing LDIF version and comments
# -b search base
# -v -o ldif-wrap=no to prevent wrapping the output
ldapsearch \
    -N \
    -H ldaps://dc1.contoso.com \
    -D mylogin@contoso.com \
    -w 'P@ssw0rd!' \
    -xLLL \
    -b DC=contoso,DC=com \
    -v -o ldif-wrap=no \
    $FILTER $ATTRIBUTES

The LDAP filter is a LDAP query on attributes:

  • * and (objectClass=*) request every available objects and their attributes

  • (cn=mylogin) requests the attributes of the object which Common Name matches “mylogin”

  • (cn=*_admin) requests the attributes of the object which Common Name ends with “_admin”

  • (objectCategory=Person) requests the attributes of all objects in category “Person”

  • (displayName=*needle*) requests the attributes of the object which display name contains “needle”

  • (memberOf=CN=Administration,OU=User Groups,OU=Company Groups,DC=contoso,DC=com) requests the member of a group

  • (&(cn=*a*)(cn=*b*)) matches objects with a Common Name containing both “a” and “b”

  • (|(cn=*a*)(cn=*b*)) matches objects with a Common Name containing either “a” or “b”

  • (&(objectClass=user)(memberOf:1.2.840.113556.1.4.1941:=CN=Domain Admins,OU=Users,DC=contoso,DC=com) requests the nested member of a group (with LDAP_MATCHING_RULE_IN_CHAIN Microsoft extension)

  • For paging: -E pr=1000/noprompt

To ignore certificate issues, this environment variable can be used: LDAPTLS_REQCERT=never.

In order to find out the search base, the scope of the search can be modified using option -s base:

# Look in the result for "namingContexts", "defaultNamingContext", etc.
ldapsearch -H ldaps://dc1.contoso.com -xLLL -b '' -s base

# ... or specify the attribute that is printed
ldapsearch -H ldaps://dc1.contoso.com -xLLL -b '' -s base namingContexts

User accounts have several interesting attributes:

  • objectClass: person, organizationalPerson, user (and top)

  • distinguishedName: full DN for the object

  • sAMAccountName: user name in SAM (Security Account Manager)

  • userPrincipalName: like an email address

  • memberOf: group memberships

  • badPwdCount: failed login attempts count

  • pwdLastSet: timestamp and the last password change (convertible to a date through LANG=C date --date="@$(( (TS/10000000)-11676009600))")

  • logonCount: connection count

  • lastLogon and lastLogoff: connection timestamps

  • adminCount: 1 when a given object has had its ACLs changed to a more secure value by the system because it was a member of one of the administrative groups

Groups have:

  • objectClass: group

  • member: group members

  • memberOf: group memberships

Computers have:

  • objectClass: computer (and top, person, organizationalPerson, user)

  • name: NetBIOS name

  • dNSHostName: DNS FQDN

  • operatingSystem

  • operatingSystemVersion

  • lastLogonTimestamp

  • servicePrincipalName: SPN, ie. services such as TERMSRV, HTTP, MSSQL, etc. The Kerberos ticket for the service (TGS) is encrypted using the NTLM password hash of the account (Impacket provides GetUserSPNs.py for Kerberoasting attack, when an SPN is attached to a user instead of a computer).

GPO have:

  • objectClass: groupPolicyContainer, container (and top)

  • displayName

  • gPCFileSysPath: path to \\contoso.com\sysvol\contoso.com\Policies\...

DNS zones are in several trees, the variable in ${...} coming from a base query (ldapsearch -b '' -s base):

  • CN=MicrosoftDNS,CN=System,${defaultNamingContext}

  • CN=MicrosoftDNS,DC=DomainDnsZones,${defaultNamingContext} (eg. ldapsearch -N -H ldaps://dc1.contoso.com -xLLL -b 'CN=MicrosoftDNS,DC=DomainDnsZones,DC=contoso,DC=com' -v -o ldif-wrap=no)

  • CN=MicrosoftDNS,DC=ForestDnsZones,${rootDomainNamingContext}

DNS zones and DNS nodes objects have:

  • objectClass: dnsZone or dnsNode

  • dc: “Domain Component”, the Fully-Qualified Domain Name (FQDN) for the object (optional)

  • distinguishedName for DNS zones: full DN for the object (eg. DC=example.org,CN=MicrosoftDNS,DC=DomainDnsZones,DC=contoso,DC=com)

  • dn for DNS nodes: full DN for the object

  • objectCategory: CN=Dns-Zone,CN=Schema,CN=Configuration,DC=contoso,DC=com or CN=Dns-Node,...

  • dnsRecord: encoded DNS records for a DNS node

Resources