Bad logon attempts in Active Directory: Track them down with PowerShell

Our series on the 20 best stories that appeared on our site in 2020 continues with this article published in May. It looks at how IT admins can leverage PowerShell to do an important task:  Checking for potentially harmful activity in Azure Active Directory. Throughout the year, our articles on how to use PowerShell — some basic, some advanced — were extremely popular. Check out the links at the bottom of this post for more PowerShell stories.

Microsoft Active Directory provides two important services: authentication and authorization. As part of the authentication process, Active Directory validates an identity before the user can access resources on the production network. There are hundreds of events taking place in an Active Directory environment. Some of the events might be related to bad logon attempts made by users and computer accounts. However, it becomes so difficult to identify the number of people who have been sending bad logon attempts unless you use an automated approach. This is where this article comes in handy. As part of this article, we explain how to use PowerShell commands and scripts to identify the users sending bad logon attempts in an Active Directory environment and also generate reports for identifying the users sending bad logon attempts.

Why check for bad logon attempts in Active Directory?

It is imperative to understand that a hacker would always attempt to log on to a system or servers in a production environment using a username and password. The password supplied with the username is authenticated by Active Directory. If Active Directory is not able to authenticate or if the password does not match with the password stored in the Active Directory database, the logon is rejected and Active Directory stores the “bad logon attempt” against that user in its database. There are two reasons why you would need a bad logon attempts report:

  1. If you have thousands of employees in your organization, as part of the cybersecurity assessment, it becomes important for you to pull out a report on the number of bad logon attempts made by every user in the Active Directory environment. The report would help you learn bad logon attempts made by every user and the “count” is very important here. If you see a few users are sending too many bad logon attempts those users can come under the suspicious category.
  2. It is certainly not always the case that a user who appears to be causing a bad logon attempt is actually the user himself. It could be an application running on the user’s PC that is trying to use the user’s credentials to authenticate against the Active Directory, or someone else trying to impersonate that user.

Tip: If you could identify the reason for bad logon attempts, you could save time investigating the cause for account lockouts.

Checking bad logon attempts for a single user account

If you have installed Active Directory PowerShell modules, you have Get-ADUser PowerShell cmdlet which can be used to check bad logon attempts sent by users. For example, this PowerShell command can be executed to check how many bad logon attempts were sent by the user:

Get-ADUser -Identity SamUser -Filter * -Properties BadLogonCount,CanonicalName

As you can see in the above command, we are checking BadLogonCount property to check the number of bad logon attempts sent by the users. It is important to understand that a normal user can send bad logon attempts due to the fact they might have forgotten or mistyped their password, but too many attempts could be considered as a suspicious activity. So, if you think that a particular user account is performing unauthorized activities, use the above command to check the bad logon counts.

Checking bad logon attempts for all user accounts in Active Directory

As part of the cybersecurity assessment, one of the responsibilities of an Active Directory administrator is to check the number of bad logon counts for each user in the Active Directory. By now, know the command that could be used to check the bad logon counts as stated in the previous section of this article, but doing it manually for all users would take a considerable amount of time. The PowerShell script below can be used to collect bad logon counts for all users in each Active Directory domain and generate a report. There are two reports generated by the script:

  • Summary report.
  • Report with username sending bad logon counts.

Both reports are located under C:\Temp directory.

Requirements to run the PowerShell script

Before you can run the PowerShell script provided as part of this article, make sure you meet these requirements:

  • You have installed Active Directory PowerShell modules on the computer from where you plan to run the script.
  • You have access to Active Directory domains. The script connects to all Active Directory domains and provides data for each domain.
  • You have created a file called C:\Temp\DomainList.DPC and specified all domains to be checked as part of the script.
  • Your account has permissions to access all domains in the current Active Directory forest.

What does the script do?

The PowerShell script performs the following operations:

  • Checks all Active Directory domains specified in the C:\Temp\DomainList.DPC file.
  • Imports Active Directory PowerShell modules into the current PowerShell session.
  • Connects to each Active Directory domain using Get-ADUser and collects the user bad logon counts.
  • Creates two files: C:\Temp\SummaryReport.CSV and C:\Temp\BadLogonAttemptsData_Data.CSV file.

Important: The script does not write anything to Active Directory domain controllers. The script just executes Get-ADUser PowerShell cmdlet, which is a read-only command, to gather required data and provide the results in the CSV file.

The PowerShell script

$TestCSVFile ="C:\Temp\SummaryReport.CSV"
Remove-Item $TestCSVFile
$UniqueTest = "BadLogonAttemptsData"
$CurrentLoc="C:\Temp"
 
$STR = "Total Users, Total Users Sending bad logon Attempts, AD Domain, Data File Location"
Add-Content $TestCSVFile $STR
 
$DataFileLocation=$CurrentLoc+"\"+$UniqueTest+"_DATA.CSV"
Remove-Item $DataFileLocation -ErrorAction SilentlyContinue
$STR = "User Location, BadLogonCount"
Add-Content $DataFileLocation $STR
 
$GDomList = "C:\Temp\DomainList.DPC"
 
$TotNo=0
$ItemCount=0
$TestText = "Please check result"
$TestStatus="Completed"
$SumVal = "NA"
$AnyGap = "No"
 
ForEach ($ThisDomain in Get-Content "$GDomList")
{
 
$AllComps = Get-ADUser -Filter * -Properties BadLogonCount,CanonicalName -Server $ThisDomain
$TotCompsNow = $AllComps.CanonicalName.Count
$TotWithBadAttempts = 0
 
ForEach ($Item in $AllComps)
{
$ThisDName = $Item.CanonicalName
$BadNumber = $Item.BadLogonCount
 
IF ($BadNumber -eq 0)
{
}
else
{
$AnyGap = "Yes"
$TotWithBadAttempts++
$STR = $ThisDName+","+$BadNumber
Add-Content $DataFileLocation $STR
}
}
 
$STR = $TotCompsNow.ToString()+","+$TotWithBadAttempts.ToString()+","+$ThisDomain+","+$DataFileLocation
Add-Content $TestCSVFile $STR
}
 
IF ($AnyGap -eq "Yes")
{
 
$TestText = "Users are sending bad logon Attempts. Please check result."
$SumVal = ""
$TestStatus="High"
 
}
else
{
 
$TestText = " "
$SumVal = ""
$TestStatus="Passed"
}

Once the script has finished executing, you can check the data in the C:\Temp folder. The two files created by the script provide details about bad logon counts. As you can see in the output below generated by the script, the summary shows the number of users in each Active Directory domain, total users sending bad logon attempts in each domain, and data file location. As you can see there are 200 users in ITRiskScan.com domain and 122 users out of 200 are sending bad logon attempts. Similarly, for Lab.ITRiskScan.com, the result shows that four out of 10 users have been sending bad logon attempts. The summary also shows the data file location.

If you need to check all who have been sending bad logon attempts, open C:\Temp\BadLogonAttempts_Data.CSV file and find out the usename and number of bad logon attempts by each user shown in the screenshot below:

As you can see in the output above, the bad logon attempt for user1 and user2 is 20 and 45, respectively. Once you have the output with you, you can get in touch with the user and check as to why there were so many bad logon attempts using their Active Directory logon credentials.

Take action

Don’t just sit there with the report. You must take action to avoid any security risks. You can take the following actions:

  1. Get in touch with the user and ask how many bad logon attempts were done in the past few days and why.
  2. Check with the user if they are using any Active Directory application that has been configured with an old password that is triggering a bad logon attempt.
  3. Check if the user installed any application recently that required them to supply their credentials during the installation.

The script that you see as part of this article is obtained from DynamicPacks IT Health Profiler, a health and risk assessment product designed to assess risk and health issues in an Active Directory environment as shown in the screenshot below:

bad logon attempts

There are many other checks that you should perform to avoid security risks and, of course, to prevent loss of business data. DynamicPacks IT Health Profiler can perform 110 Active Directory related checks and create an actionable report that contains issue details and recommendations to fix the issues.

Don’t go from bad to worse

In this article, we provided a way to check bad logon attempts in Active Directory. We showed you that Active Directory stores the bad logon attempts generated by users in an attribute called BadLogonCount. We provided a PowerShell script that could be used to collect bad logon data from the Active Directory and generate a report in CSV format.

Featured image: Shutterstock

About The Author

10 thoughts on “Bad logon attempts in Active Directory: Track them down with PowerShell”

  1. This is the result of running the script:

    Get-ADUser : Cannot validate argument on parameter ‘Server’. The argument is null or empty. Provide an argument that is
    not null or empty, and then try the command again.
    At C:\Users\Administrator\Desktop\badlogon.ps1:26 char:82
    + … -Filter * -Properties BadLogonCount,CanonicalName -Server $ThisDomain
    + ~~~~~~~~~~~
    + CategoryInfo : InvalidData: (:) [Get-ADUser], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.ActiveDirectory.Management.Commands.GetADUser

  2. Thanks for reading @S.Weel.

    You need to create a file that contains domain names in your Active Directory forest. Please create C:\Temp\DomainList.DPC and mention all domains in it.

    Please let me know if you still face any errors.

    Thanks,
    Nirmal

  3. Harry van Heydoorn

    How do I create DomainList.DPC? Meaning Do I create a text file and save as DomainList.DPC? What do I add in the text file? Just the name of the domain? test.com

  4. Nirmal Sharma

    Hi Harry, Yes exactly. Let me know if you face any issues while running the script.

    Thanks,
    Nirmal

  5. Hi Nirmal,

    Great script! Can you speak to what action results in the BadLoginCount being reset to 0? It seems from my testing that simply inactivating the user will not reset this number. It appears the user has to actually log in successfully 1 time for the count to reset to 0.

    Can you speak to this more and detail the different ways the count can reset to 0?

    1. Hi Paul,
      Sorry have taken a very long time to reply to your comment – let me write another post on BadLogon specifically.

      Thanks,
      Nirmal

  6. Does this also return the IP address of the attempt… or just a “count”? How is that called “track them down” as the title said?

  7. This doesn’t list anywhere near the number of users I have in the summary of bad logons , but it reports the number correctly.

  8. I’m sorry to say this, but your script doesn’t do a very good job at looking for bad logon attempts. It needs to look at each Domain Controller to see how many there are. I would also suggest not hand building strings as that is not a PowerShell pattern. Instead utilize things like Export-Csv or ConvertTo-Html. I’ve used both of those many times before. ConvertTo-Html is useful for automatic email report to make them look nicer, but Export-Csv is what I use the most. If someone wants some data, I export it to a Csv and just give that to them. Then they can open it in Excel to look at it. It isn’t as pretty in some cases, but it is VERY functional and quick.

    Here’s an example that should get you a better picture of the bad logon count across Domain Controllers.

    $Domains = ‘Domain1’, ‘Domain2’, ‘Domain3’
    $BadLogons = $Domains |
    ForEach-Object {
    Get-ADDomainController -Server $_ -Filter “Name -like ‘*'” |
    ForEach-Object {
    Write-Verbose $_.hostname -Verbose
    $ADServer = $_.hostname
    Get-ADUser -Server $ADServer -Filter “badlogoncount -gt 0” -Properties CanonicalName, BadLogonCount |
    Select-Object *, @{n=’ADServer’;e={ $ADServer}}
    }
    }

    $BadLogons |
    Group-Object CanonicalName |
    Select-Object @{n=’CanonicalName’;e={ $_.Name}}, @{n=’BadLogonCount’;e={ $_.Group | Measure-Object -Sum BadLogonCount | ForEach-Object { $_.Sum } }} |
    Sort-Object CanonicalName |
    Export-Csv -NoTypeInformation -Path C:\BadLogonReport.csv

Leave a Comment

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Scroll to Top