In the introductory post, we outlined what a service account is and how these accounts relate to other privileged accounts within an Active Directory environment. There could be many reasons to discover where service accounts are and how they are being used. In this post, we will approach this discovery through the mindset of an attacker. Attackers will often target service accounts because they hold elevated privileges and do not have strict password reset policies. That allows these accounts to be compromised and leveraged for extended periods of time without detection or hindrance.
During the reconnaissance phase of the kill chain, an attacker will have little to no privileges within the domain. Therefore, any discovery performed must be possible without elevated privileges such as Domain Administrator or even local Administrator rights. Let’s look at some of the ways an attacker can find service accounts with no privileges at all.
Active Directory offers many security and management benefits, but also can make things a little too easy for a curious attacker. Due to the architecture of Active Directory, once an attacker has infiltrated any domain-joined computer, they are able to query the directory and its objects. In some cases, this can even be done anonymously. Here are some of the ways an attacker can discover service accounts by querying the directory.
Service accounts leverage SPNs to support Kerberos authentication. While this provides improved security, it also leaves a trail of exactly where these accounts are used and what they are used for. This information can be easily exploited by an attacker. SPNs are commonly used to run services to support applications like Microsoft SQL Server and SharePoint. In a previous blog series, we explored how to use PowerUpSQL to perform a more advanced discovery based on this principle. For our purposes, there are even simpler ways to get the information we need.
Using PowerShell, it is possible to find a list of all domain service accounts that have registered SPN values.
#Build LDAP Filter to look for users with SPN values registered for current domain
$ldapFilter = "(&(objectclass=user)(objectcategory=user)(servicePrincipalName=*))"
$domain = New-Object System.DirectoryServices.DirectoryEntry
$search = New-Object System.DirectoryServices.DirectorySearcher
$search.SearchRoot = $domain
$search.PageSize = 1000
$search.Filter = $ldapFilter
$search.SearchScope = "Subtree"
#Execute Search
$results = $search.FindAll()
#Display SPN values from the returned objects
foreach ($result in $results)
{
$userEntry = $result.GetDirectoryEntry()
Write-Host "User Name = " $userEntry.name
foreach ($SPN in $userEntry.servicePrincipalName)
{
Write-Host "SPN = " $SPN
}
Write-Host ""
}
And you can see from the results the SPN value itself will show you where the account is registered and what service it is registered for on that system. Below is the SPN value for a SQL service account.
With this simple query an attacker can identify a list of service accounts as well as the computers, applications, and data they will provide access to.
While SPNs are very reliable and informative, they will not produce a comprehensive list of service accounts. Many accounts do not integrate with Kerberos through SPNs and will have no SPN values set. However, most organizations find other ways to make the discovery of service accounts achievable from the directory through use of naming and location conventions.
Most companies will establish naming conventions where all service accounts start with “SVC” or something similar. Also, service accounts are typically placed into their own organizational units (OUs) or groups. By exploiting these common conventions, attackers can again discover valuable service accounts with no domain rights.
Here is a PowerShell script to find all accounts that contain “svc” in the name. This can easily be modified to support other naming conventions.
#Build LDAP Filter to look for users with service account naming conventions
$ldapFilter = "(&(objectclass=Person)(cn=*svc*))"
$domain = New-Object System.DirectoryServices.DirectoryEntry
$search = New-Object System.DirectoryServices.DirectorySearcher
$search.SearchRoot = $domain
$search.PageSize = 1000
$search.Filter = $ldapFilter
$search.SearchScope = "Subtree"
#Adds list of properties to search for
$objProperties = "name"
Foreach ($i in $objProperties){$search.PropertiesToLoad.Add($i)}
#Execute Search
$results = $search.FindAll()
#Display values from the returned objects
foreach ($result in $results)
{
$userEntry = $result.GetDirectoryEntry()
Write-Host "User Name = " $userEntry.name
Write-Host ""
}
If naming conventions for the objects are not used, often times it is valuable to search for where they are located within the directory structure. This LDAP filter can be replaced into the above script to find any OUs that contain “Service” or “svc” in the name:
Once you know those locations, you can search those OUs for all user objects to find your service accounts. This script is updated to focus on one OU with Service in the name:
Once you know those locations, you can search those OUs for all user objects to find your service accounts. This script is updated to focus on one OU with Service in the name:
Another sneaky way to search Active Directory for service accounts is to investigate the values of an object’s user account control settings. Often, service accounts will have settings in place that regular user accounts will not. The best example of this is the “password never expires” setting. Service accounts may have passwords set to never expire because the act of resetting them can be tedious and result in application or service outages.
With PowerShell it is possible to query all accounts with this value enabled, using a slightly more complicated LDAP filter.
While this post focused solely on ways to find service accounts without leveraging any privileges, if an attacker does find an account that has privileges on one or more systems within the network, there are many other effective ways to discover service accounts. Some of those ways include:
Now that we have built a list of valuable service accounts, the next step is to exploit these accounts. We will explore techniques to do so in the upcoming blog posts:
Service Account Attack #2 – Extracting Service Account Passwords with Kerberoasting Read Now
Service Account Attack #3 – Targeted Service Account Exploitation with Silver Tickets Read Now
Service Account Attack #4 – Exploiting the KRBTGT service account for Golden Tickets Read Now
To watch the Service Account Attacks webinar, please click here.
Jeff Warren is Stealthbits’ General Manager of Products. Jeff has held multiple roles within the Technical Product Management group since joining the organization in 2010, initially building Stealthbits’ SharePoint management offerings before shifting focus to the organization’s Data Access Governance solution portfolio as a whole. Before joining Stealthbits, Jeff was a Software Engineer at Wall Street Network, a solutions provider specializing in GIS software and custom SharePoint development.
With deep knowledge and experience in technology, product and project management, Jeff and his teams are responsible for designing and delivering Stealthbits’ high quality, innovative solutions.
Jeff holds a Bachelor of Science degree in Information Systems from the University of Delaware.
Learn why Active Directory security should be a priority for your organization and ways to mitigate against a data breach with this free white paper!
Read more© 2021 Stealthbits Technologies, Inc.
Screenshots? Seriously?
That makes the article a lot less useful by preventing copying and pasting the article’s code into the user’s ISE…
You’re right! I went back over the post and converted from pictures to code snippets so you can copy and paste them now. Hope that helps.
Hello Jeff. I’m finding this article very useful. I’m not knowledgeable with PS, and tried to research and tried a few code inserts but couldn’t get it to work, but I was wondering how you could write the output of the first script in this article (Service Principal Names (SPNs)), out to a file in my local directory, instead of to the screen?
Hello, glad you are finding this useful! With a few minor changes to the script you can easily store the values in a string and output it to a file rather than print it to a screen. Here is the same script which will write to a file c:\Temp\SPN.txt:
#Build LDAP Filter to look for users with SPN values registered for current domain
$ldapFilter = "(&(objectclass=user)(objectcategory=user)(servicePrincipalName=*))"
$domain = New-Object System.DirectoryServices.DirectoryEntry
$search = New-Object System.DirectoryServices.DirectorySearcher
$search.SearchRoot = $domain
$search.PageSize = 1000
$search.Filter = $ldapFilter
$search.SearchScope = "Subtree"
#Execute Search
$results = $search.FindAll()
#Display SPN values from the returned objects
$resultOutput = @()
foreach ($result in $results)
{
$userEntry = $result.GetDirectoryEntry()
$resultOutput += "User Name = " + $userEntry.name
#Write-Host "User Name = " $userEntry.name
foreach ($SPN in $userEntry.servicePrincipalName)
{
$resultOutput += "SPN = " + $SPN
#Write-Host "SPN = " $SPN
}
#Write-Host ""
}
Out-File -FilePath C:\temp\SPN.txt -InputObject $resultOutput
Worked like a charm Jeff!!!! Much thanks. Take care. John.
Hello Jeff.
Is there a way to use ‘Service Accounts Discovery via User Account Control’, to find e.g. svc accounts with a ‘blank’ password? Cheers, John.
Jeff. I continued researching this, and I don’t belive what I asked is possible. What I did find is that you can use ‘User Account Control’ to find AD accounts that have the ‘Password Required’ set to ‘No’, which could use a ‘blank’ password. This basically overrides the group policy/password controls:
$ldapFilter = “(&(objectclass=user)(objectcategory=user)(useraccountcontrol:1.2.840.113556.1.4.803:=32))”
Hey John, Glad you got that working and thanks for sharing!
Hi Jeff,
thanks for the article – very useful to someone who has no knowledge of AD and is trying to root a Windows box on HTB!
Thank you
Angus