Introduction
During a recent IT audit in an enterprise organization, the tasks included verification of database user ID's to ensure they belong to active, valid users. With the continuing trend of deploying more and more applications on WINTEL platforms, the majority of databases are likewise on WINTEL, their user access being set up through centralized Microsoft Windows Active Directory groups.
I faced the problem of how to extract the users belonging to various domain groups: Located in a remote location, having a difficult timezone offset, without access to local user ID verification records and support teams, I had to find a way to extract user ID data independently, which would also be most efficient.
The following three factors have been a great help:
- Deficiencies of internal access control and network security in enterprise organisations
Enterprise organisations tend to face the problem of size, work segregation and loss of personal responsibility. This frequently leads to sub-par IT configurations, particularly with the devaluation of Information Technology work through cost reduction and outsourcing.
- Microsofts tendency of delivering out-of-the-box functionality over security
As we will see below, Microsoft provides common access to Active Directory data by default
- A general unawareness, how useful AD data can be in the 'right' (or 'wrong') hands
While AD data is sometimes hard to decipher, knowledge how to interpret and use it can make it a very powerful source of core data.
Prerequisites
Let's start with a Windows 7 laptop as a domain member, together with a local domain user account.
The first step of validating users in Windows Active Directory is to become familiar with how to access and extract user data within your local Domain. A Windows domain has a centralized database located on dedicated Windows servers, the Domain Controllers. We need to get the name of our local domain, plus the IP's of the controllers.
Extracting the Domain Name
This step is often unnecessary, since domain names are typically part of the user login and often well-known. To be sure, we can open the Windows builtin command prompt on our destop and run:
C:\>net config workstation Computer name \\HUSDE583752 Full Computer name husde583752.frank4dd.com User name frankme Workstation active on NetBT_Tcpip_{4BCFB712-EEBA-45F9-90F2-5275245E4C4B} (F0DEF177C1D1) Software version Windows 7 Enterprise Workstation domain MYDOMAIN3 Workstation Domain DNS Name frank4dd.com Logon domain MYDOMAIN3 COM Open Timeout (sec) 0 COM Send Count (byte) 16 COM Send Timeout (msec) 250 The command completed successfully.
We note the 'Logon domain' (NetBIOS domain name) and the 'Domain DNS Name' values.
Extracting the Domain Controller IP Address
The next step will give us the local domain controllers IP address, the second important piece of information we need. Here, we use a very helpful program called nltest. In my case it came with Windows 7 pre-installed, XP users possibly need to download a package called Support Tools from Microsoft Technet. More information about 'nltest.exe' is available here: Confirming Domain and Workgroup Membership
C:\>nltest /DCLIST:MYDOMAIN3 Get list of DCs in domain 'MYDOMAIN3' from '\\JPNHOMG001'. pdc01.frank4dd.com [PDC] [DS] Site: Default-First-Site-Name bdc02.frank4dd.com [DS] Site: Default-First-Site-Name OLDSERVER2 bdc03.frank4dd.com [DS] Site: Default-First-Site-Name The command completed successfully
With knowledge of the DNS name, let's make a quick translation to the IP through 'ping' or 'nslookup'.
C:\>ping pdc01.frank4dd.com -n 1 Pinging pdc01.frank4dd.com [192.168.100.21] with 32 bytes of data: Reply from 192.168.100.21: bytes=32 time=1ms TTL=127
Checking domain controller's LDAP network access
After locating the IP's for the domain database, let's review our access options. Active Directory, introduced with Windows 2000, is an LDAP-based directory service. LDAP (Lightweight Directory Access Protocol) is a network service with standard ports '389/TCP' and '636/TCP' (SSL-encrypted).
For more information about LDAP, one of the best presentations is "Directory-enabled Applications" from Netscape.
Windows domain controllers come with LDAP available on port 389 by default. (Ref. Active Directory Domain Services)
The default at port 389 means that Active Directory access is cleartext only, and the encrypted LDAPS protocol needs to be explicitly enabled. (Ref. How to enable LDAP over SSL)
We check if the default LDAP network port is available to us. I am using a simple 'telnet', the technically inclined could use specific tools (i.e. port scanners like nmap).
C:\>telnet bdc02.frank4dd.com 389 ...
Access failure could cause this response:
Connecting To bdc02.frank4dd.com...Could not open connection to the host, on port 389: Connect failed
Note:i In Windows 7 the 'telnet' command is no longer standard, but requires extra installation i.e. by using the "Features" wizard.
Domain controller's LDAP user access
Knowing the Active Directory IP and port, how do we know which users can user access? Since Windows 2003, Microsoft allows default access for *all* authenticated domain users. Before 2003, even anonymous access was accepted. (Ref: Anonymous LDAP operations to Active Directory)
Given that a Windows domain account is a standard, and usually provisioned to all users (often including contractors, etc), there is nothing left in the way of anybody "reading" one of the most critical, central, access-granting databases in the organization.
We will test user access soon, lets first select a suitable client program for LDAP.
LDAP access software
The following LDAP client programs are provide easy data visualization through a graphical interface. Below is a list of three freely available, popular browsers:
1. Softerra LDAP Browser 4.5
License: free for any use, including commercial
Download: http://www.ldapbrowser.com
+ 'read-only' design for worry-free access
+ Can do Kerberos (GSS) authentication to access DC in remote, trusted domains
- Sometimes showing values as "unspecified" when they are in fact '0' for a reason)
2. Apache Directory Studio
License: Apache License 2.0
Download: http://directory.apache.org
- Could not connect to remote DC through trusted domains, using Kerberos (GSS)
+ Powerful export functions
+ Java-based, platform independent
3. LDAP Admin
License: GNU General Public License
Download: http://www.ldapadmin.org
+ Can do Kerberos (GSS) authentication to access DC in remote, trusted domains
+ Correctly displays true value, i.e. zero vs. unset
- Simple, although sufficient functions
Connecting to AD
With access parameters, and client software, we can now access AD. Let's review the LDAP-typical authentication methods and options:
First we specify the IP or DNS name of the Domain Controller (left image). We can use the standard port 389, and additionally we need to give the Base-DN (DN=Distinguished Name). For Windows AD, this typically refers to the Domain's DNS name. In this example, it would be DC=frank4dd,DC=com.
For credentials (right image), the easiest way to connect is to select Currently logged on user (Active Directory only). It is also possible to select "Simple". There, the username is a construct of the Windows user ID, followed by '@' and the Domain DNS name. This construct is called userPrincipalName, and although it may look like an email address, it is not necessarily identical. The 'Simple' authentication method is most useful for scripting against AD's LDAP, and serves only as a example for cases when built-in authentication is not available.
Querying Data in Active Directory
After login, we have to navigate the LDAP structure. Active Directory organizes its data objects (i.e. users, groups, computers) hierarchical, requiring to drill down to the actual records. The records format is predefined through classes and attributes in a specific LDAP schema. (Ref. Active Directory Schema)
Because it is easy to get confused by terminology, let's look at an example domain user account record. Below, a domain user account has been extracted from AD, showing the typically available information. Navigating the tree down to the Users default container, we typically find domain user accounts below it. Under the Users container, administrators could organize accounts in subcontainers by department, role, etc. In our example, the sub-container (OU = organizational unit) called IT_department has been created.
Besides users, we find wealth of information about computers in Active Directory. First, there are the systems that have been joined to the domain: domain members. Then, it is quite typical to run a domain controller also as a DNS server. While DNS security often restricts anonymous zone transfers (which allows to see all systems registered in DNS), don't despair. All DNS records are nicely visible in AD, and we can extract all DNS zone information from there.
Active Directory user ID example:
Name | Value |
---|---|
objectClass | top |
objectClass | person |
objectClass | organizationalPerson |
objectClass | user |
cn | frankme |
sn | Me |
description | Employee ID #A320021 |
givenName | Frank |
distinguishedName | CN=frankme,OU=IT_Department,OU=User,DC=frank4dd,DC=com |
instanceType | 4 [ Writable ] |
whenCreated | 20080215211605.0Z [ 2008/02/15 21:16:05 ] |
whenChanged | 20121217003856.0Z [ 2012/12/17 0:38:57 ] |
displayName | Frank Me |
uSNCreated | 21128 |
memberOf | CN=acl_it_staff,OU=Information Technology,OU=User,DC=frank4dd,DC=com |
memberOf | CN=acl_dept_fileshares,OU=Security - Global,OU=User,DC=frank4dd,DC=com |
memberOf | CN=acl_it_architecture,OU=Information Technology,OU=User,DC=frank4dd,DC=com |
uSNChanged | 58452002 |
name | frankme [ this is an obsolete Windows 2000 attribute] |
objectGUID | {3CE3E514-E038-44E3-8C4C-41D5E37A6099} |
userAccountControl | 512 [ NormalAccount ] |
badPwdCount | 2 |
codePage | 0 |
countryCode | 0 |
badPasswordTime | 130005249549020623 [ 2012/12/21 0:55:54 ] |
lastLogoff | 0 [ unspecified ] |
lastLogon | 130005249613083123 [ 2012/12/19 7:03:43 ] |
scriptPath | logonNoProxy.bat |
logonHours | FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF |
pwdLastSet | 130001783218449183 [ 2012/12/17 0:38:41 ] |
primaryGroupID | 513 |
objectSid | S-1-5-21-1047594904-1774598210-1845911597-17667 |
accountExpires | 0 [ never ] |
logonCount | 21282 |
sAMAccountName | frankme |
sAMAccountType | 805306368 [ samUserAccount ] |
userPrincipalName | frankme@frank4dd.com |
lockoutTime | 0 [ unspecified ] |
objectCategory | CN=Person,CN=Schema,CN=Configuration,DC=frank4dd,DC=com |
lastLogonTimestamp | 129998313117351145 [ 2012/12/13 0:15:11 ] |
frankme@frank4dd.com |
The following account attribute records are particularly interesting:
- description - Often used to carry additional user information, such as employee ID, birthdate, termination or suspension
- memberOf - Typically several records exist, adding a user ID to a particular Windows domain group, often used as a ACL
- lockoutTime - Time of the account lock out after too many failed password entries
- pwdLastSet - The Date/time of the last password change.
Tip: Avoid getting excited here. It is tempting to test, using your least favorite colleague's credentials and lock them out, all while watching the increase of badPWDCount :-). Remember the Windows eventlog might be reviewed.
Searching Data in Active Directory
In large organisations it is not uncommon to have thousands, or ten thousands of user records. We quickly realize that just browsing our way through the LDAP tree is not efficient. In Active Directory, the number of records returned in one query is limited to 1.000. If the query returns more, a 'limit exceeded' message is returned. Bad luck if our record is number 1001. We really need to learn how to filter and search our data in Active Directory. LDAP search filters have their own syntax, and below are example queries to get started. See also Search Filter Syntax.
- Search for a specific group (acl): (&(objectClass=group)(cn=Domain Admins))
- Check for "locked out" users: (&(objectClass=user)(lockoutTime>0))
(This is not 100% correct, but exceptions are quite rare)
- Check all users created since date X (i.e. 7 days ago): (&(objectClass=user)(whenCreated>=20121215*))
- Check if servicedesk creates new users with "password change on next logon" enabled:
(&(!(objectClass=Computer))(objectClass=person)(whenCreated>=20121123000000.0Z))Similar to the query above, this query will bring up a list of users that have been created on or later then the timestamp in our selection. A good time reference is to go one or two weeks back. The number of user additions might vary greatly, depending on the organisation. In large enterprises, it is not uncommon to have 20 users added within that period.
In looking over the returned user list, we will focus on the attributes lastLogon and pwdLastSet.
If the domain account was created but the user did not log on yet, lastLogon will be zero or empty. During first login, this value will change into the current timestamp.
If users are required to change their passwords at first login, the timestamp for pwdLastSet will be very close to the value for lastLogon.
If the user has not logged on yet, the value for pwdLastSet will be zero if the checkbox for "Change password at next logon" has been set. See also Microsoft Technet - Configuring a Password Change at Next Logon Requirement.
Accessing AD in Trusted Domains
In large enterprise organisations, we typically find multiple Windows domains, connected to each other through the principle of "trust". These "trust" relationships allow granting access accross organizational boundaries. The same "trust" will also allow us look into the domain controller of a different part in our organization, listing their users, groups, and systems.
Domain "trust" is also a dangerous feature that could expose remote organizations network file shares, but this discussion is a separate topic. Just be aware that Windows built-in functional ID's such as "Everyone" and "Authenticated Users" work great accross domain boundaries.
If you google the topic about accessing LDAP on a remote, trusted domain, most respondents will say that this is not possible. I did this frequently, and it works. There is one restriction: You'll need to use the Windows-builtin Kerberos authentication mechanism. No other authentication method can connect you, and this rules out a lot of third-party scripts or tools. But lets start. Again, we need to gather some information before we can connect:
- What are our trusted domains?
Here, the nltest command will help us again to identify the available choices:
D:\Code\DNS>nltest /DOMAIN_TRUSTS List of domain trusts: 0: MYDOMAIN1 amer.partner.net (NT 5) (Direct Outbound) (Direct Inbound) ( Attr: quarantined 1: D2PRD prd.supplier2.com (NT 5) (Direct Outbound) (Direct Inbound) ( Attr: quarantined ) 2: CHDMP china.f4dd.com (NT 5) (Direct Outbound) (Direct Inbound) ( Attr: quarantined ) 3: MYDOMAIN3 frank4dd.com (NT 5) (Forest Tree Root) (Primary Domain) (Native) The command completed successfully
- What are the trusted domain controllers?
D:\Code\DNS>nltest /DCLIST:amer.partner.net Get list of DCs in domain 'amer.partner.net' from '\\KDQDCP01.amer.partner.net'. PSMDCP01.amer.partner.net [DS] Site: PSM PSMDCP02.amer.partner.net [DS] Site: PSM KDQDCP01.amer.partner.net [DS] Site: KDQ B04DCP05.amer.partner.net [PDC] [DS] Site: B04 The command completed successfully
And viola, we got all DNS names of the domain controllers. Since the domains are trusted, DNS is often replicated or at least forwarded, so getting the trusted domain controllers IP address should not be an issue. Typically, a simple 'ping' works at this point and returns us the IP.
- Are there network restrictions preventing access to LDAP ports?
For this verification, we simply do another port access check as documented in "Prerequistes" above, just to make sure. Sometimes, a firewall really blocks. This is a mere accident, because internal firewalls are mostly just IT's decorations.
Finally, we connect to the remote domain controllers with our LDAP client, same as if they where local. Remember only the Windows-integrated Kerberos authentication will work. Some clients call it "Windows-integrated", others call it GSS-API. Not all clients handle this method well, I had trouble using it with Apache Directory Studio (Java).
Conclusion
With some background about LDAP technology, Active Directory in its default configuration is a open book to anybody who cares, and the information can be both useful or dangerous. For user verification and auditing, querying the true source is ideal, as it provides core data independent from the auditee.
More information and articles around this topic:
- Active Directory information exposed to users?
- Microsoft Active Directory Best Practice
- 19 Smart Tips for Securing Active Directory (a little older, but still useful)