Spray365 is a password spraying tool that identifies valid credentials for Microsoft accounts (Office 365 / Azure AD).

Overview

What is Spray365?

Spray365 is a password spraying tool that identifies valid credentials for Microsoft accounts (Office 365 / Azure AD). How is Spray365 different from the many other password spraying tools that are already available? Spray365 enables passwords to be sprayed from an "execution plan". While having a pre-generated execution plan that describe the spraying operation well before it occurs has many other benefits that Spray365 leverages, this also allows password sprays to be resumed (-R option) after a network error or other interruption. While it is easiest to generate a Spray365 execution plan using Spray365 directly, other tools that produce a compatible JSON structure make it easy to build unique password spraying workflows.

Spray365 exposes a few options that are useful when spraying credentials. Random user agents can be used to detect and bypass insecure conditional access policies that are configured to limit the types of allowed devices. Similarly, the --shuffle_auth_order argument is a great way to spray credentials in a less-predictable manner. This option was added in an attempt to bypass intelligent account lockouts (e.g., Azure Smart Lockout). While it’s not perfect, randomizing the order in which credentials are attempted have other benefits too, like making the detection of these spraying operations even more difficult. Spray365 also supports proxying traffic over HTTP/HTTPS, which integrates well with other tools like Burp Suite for manipulating the source of the spraying operation.

Generating an Execution Plan (Step 1)

Generating Execution Plan

Spraying Credentials with an Execution Plan (Step 2)

Spraying Execution Plan

Getting Started

Installation

Clone the repository, install the required Python packages, and run Spray365!

$ git clone https://github.com/MarkoH17/Spray365
$ cd Spray365
~/Spray365$ pip3 install -r requirements.txt
~/Spray365$ python3 spray365.py

Usage

Generate an Execution Plan

An execution plan is needed to spray credentials, so we need to create one! Spray365 can generate its own execution plan by running it in "generate" (-g) mode.

$ python3 spray365.py -g <path_for_saved_execution_plan> -d <domain_name> -u <file_containing_usernames> -pf <file_containing_passwords>

e.g.

$ python3 spray365.py -g ex-plan.s365 -d example.com -u usernames -pf passwords

Spraying an Execution Plan

Once an execution plan is available, Spray365 can be used to process it. Running Spray365 in "spray" (-s) mode will process the specified execution plan and spray the appropriate credentials.

$ python3 spray365.py -s <path_to_execution_plan>

e.g.

$ python3 spray365.py -s ex-plan.s365

Other Options for Advanced Usage

Generate Mode Options

--delay : Delay in seconds to wait between authentication attempts (default: 30)

-cID / --aad_client : Client ID to use during authentication workflow (None for random selection, specify multiple in a comma-separated string) (default: None)

-eID / --aad_endpoint : Endpoint ID to use during authentication workflow (None for random selection, specify multiple in a comma-separated string) (default: None)

-S / --shuffle_auth_order: Shuffle order of authentication attempts so that each iteration (User1:Pass1, User2:Pass1, User3:Pass1) will be sprayed in a random order, and with a random arrangement of passwords, e.g. (User4:Pass16, User13:Pass25, User19:Pass40). Be aware this option introduces the possibility that the time between consecutive authentication attempts for a given user may occur as quickly as DELAY seconds apart. Consider using the -mD / --min_cred_loop_delay option to enforce a minimum delay between authentication attempts for any given user. (default: False)

-SO / --shuffle_optimization_attempts : Number of random execution plans to generate for identifying the fastest execution plan (default: 10)

-mD / --min_cred_loop_delay : Minimum time to wait between authentication attempts for a given user. This option takes into account the time one spray iteration will take, so a pre-authentication delay may not occur every time (disable with 0) (default: 0)

-cUA / --custom_user_agent : Set custom user agent for authentication requests (default: None)

-rUA, --random_user_agent: Randomize user agent for authentication requests (default: False)

Spray Mode Options

--lockout : Number of account lockouts to observe before aborting spraying session (disable with 0) (default: 5)

--proxy : HTTP Proxy URL (format: http[s]://proxy.address:port) (default: None)

-R / --resume_index : Resume spraying passwords from this position in the execution plan (default: 0)

Acknowledgements

Author Tool / Other Link
@__TexasRanger msspray: Conduct password spray attacks against Azure AD as well as validate the implementation of MFA on Azure and Office 365 endpoints https://github.com/SecurityRiskAdvisors/msspray

Disclaimer

Usage of this software for attacking targets without prior mutual consent is illegal. It is the end user’s responsibility to obey all applicable local, state and federal laws, in addition to any applicable acceptable use policies. Using this software releases the author(s) of any responsiblity for misuse or damage caused.

Comments
  • Python error str and None

    Python error str and None

    Ubuntu 20.04 Python 3.8.10

    Running into this issue when attempting to run spray365.

    $ python3 spray365.py
    
    Traceback (most recent call last):
      File "spray365.py", line 2, in <module>
        from modules.core.output import console
      File "/home/nancea/Spray365/modules/core/output/console.py", line 74, in <module>
        line_terminator: str | None,
    TypeError: unsupported operand type(s) for |: 'type' and 'NoneType'
    

    EDIT to fix formatting

    opened by anance25 6
  • Spraying Stopped due to Error

    Spraying Stopped due to Error

    Hi there,

    Sometimes it happens, but not always. I get an error that stops the spraying.

    An error occured while spraying credentials: 'Unable to get authority configuration for https://login.microsoftonline.com/organizations. Authority would typ
     https://login.microsoftonline.com/your_tenant_name'
    
    opened by vysecurity 3
  • An error occured while spraying credentials: ''NoneType' object has no attribute 'error_code'

    An error occured while spraying credentials: ''NoneType' object has no attribute 'error_code'

    Getting this error right after "Authentication Success" for a valid M365 user in the tenant:
    ERROR]: An error occured while spraying credentials: ' 'NoneType' object has no attribute 'error_code'

    I'll try to debug this in the code for why this is happening. When I proxy the request through Burp Suite it shows a successful OAuth code flow with a bearer token response. So authentication is working, but it might be an issue with parsing the response. Don't know why the code is looking for an "error_code" when it is a success.

    This looks like a great tool @MarkoH17 - thanks for the contribution.

    opened by iknowjason 3
  • Ability to Spray Multiple Tenants

    Ability to Spray Multiple Tenants

    Describe The Problem To Be Solved Currently, the user has to start separate Spray365 instances if targeting multiple organizations/tenants.

    Additionally, smart-lockout controls could potentially lean on the fact that multiple failed attempts are occurring in succession for a single organization.

    Suggested Solution To fix this issue, Spray365 should allow for:

    • The creation of execution plans targeting multiple domains with a much larger list of users
    • The creation of execution plans using a user list containing emails. Currently, the domain for the targeted tenant must be specified using a separate CLI flag
    • Show the specific domain being targeted in stdout

    Again, similar to the other issue I created, getting a handle on where I would implement this functionality is proving difficult. Let me know your thoughts on this and if I can provide any assistance. Thanks!

    enhancement 
    opened by puzzlepeaches 2
  • Alternate behavior for --lockout flag

    Alternate behavior for --lockout flag

    Describe The Problem To Be Solved Smart-lockout is the biggest issue faced when password spraying Microsoft services. While a general assumption on my part, I assume that this security control is based less on source IP and more on the frequency of failed login attempts for a tenant.

    Currently, Spray365 exits entirely after a lockout threshold is met which does not work well for long running password sprays as the job must be manually started again to continue.

    Suggested Solution To fix this issue, Spray365 should provide the option to pause for an elongated period of time before continuing with the password spray. For example, if the specified lockout threshold is met, pause execution for two hours before continuing again.

    Even more ideally, allow this pause time frame to dynamically increase every time the original lockout threshold is met. For example, the following spray workflow could occur:

    • A lockout threshold of 10 is set on initial execution along with a pause spray time period of two hours
    • A password spray is started and after approximately 100 attempts, the lockout threshold is met
    • The utility pauses the password spray for two hours as specified
    • After the time frame has elapsed the spray is started
    • Again after another 200 attempts, the lockout threshold is met and the tool pauses for four hours now instead of two
    • After the threshold is met X number of times and the paused execution time frame is expanded Y times, the password spray exits

    Ideally, this dynamically expanding pause execution timeframe and the condition used to determine if the tool should exit, could also be added as command line options on run.

    While the code in Spray365 is very well written, getting a handle on where I would implement this functionality is proving difficult. Let me know your thoughts on this and if I can provide any assistance. Thanks!

    enhancement 
    opened by puzzlepeaches 2
  • Fix NoneType has not attribute error

    Fix NoneType has not attribute error

    Here is my proposed fix. In some cases authentication success does not have a result.auth_erorr. Therefore it was failing here. Just had a simple check for pass if it is None:

        if result.auth_error is None:
            pass
        else:
            if result.auth_error.error_code == 50053:
                global_lockouts_observed += 1
    
    opened by iknowjason 0
  • WsTrust server returned error in RSTR

    WsTrust server returned error in RSTR

    Hello,

    I have used:

    python3.10 spray365.py generate normal -ep ex-plan.s365 -d dom.de -u users.txt -pf pwds.txt
    

    It is a fresh install.

    python3.10 spray365.py spray -ep ex-plan.s365                                                    
    
    ███████╗██████╗ ██████╗  █████╗ ██╗   ██╗██████╗  ██████╗ ███████╗
    ██╔════╝██╔══██╗██╔══██╗██╔══██╗╚██╗ ██╔╝╚════██╗██╔════╝ ██╔════╝                                                                                                                                                                           
    ███████╗██████╔╝██████╔╝███████║ ╚████╔╝  █████╔╝███████╗ ███████╗                                                                                                                                                                           
    ╚════██║██╔═══╝ ██╔══██╗██╔══██║  ╚██╔╝   ╚═══██╗██╔═══██╗╚════██║                                                                                                                                                                           
    ███████║██║     ██║  ██║██║  ██║   ██║   ██████╔╝ ██████╔╝███████║                                                                                                                                                                           
    ╚══════╝╚═╝     ╚═╝  ╚═╝╚═╝  ╚═╝   ╚═╝   ╚═════╝  ╚═════╝ ╚══════╝                                                                                                                                                                           
                             By MarkoH17 (https://github.com/MarkoH17)                                                                                                                                                                           
                                                   Version: 0.2.2-beta                                                                                                                                                                           
                                                                                                                                                                                                                                                 
    [2022-08-05 06:37:15 - INFO]: Processing execution plan 'ex-plan.s365'
    [2022-08-05 06:37:15 - INFO]: Identified 18650 credentials in the provided execution plan
    [2022-08-05 06:37:15 - INFO]: Password spraying will take at least 559500 seconds, and should finish around 2022-08-11 18:02:15
    [2022-08-05 06:37:15 - INFO]: Lockout threshold is set to 10 accounts
    [2022-08-05 06:37:15 - INFO]: Starting to spray credentials
    An exception was raised: RuntimeError650] (win_ie11_win8->webshellsuite->outlook): testaccount / thepassword (waiting...)
    Stack trace from most recent exception:
    Traceback (most recent call last):
      File "/home/myaccount/tools/Spray365/modules/spray/spray_exception_wrapper.py", line 13, in invoke
        return super(SprayExceptionWrapper, self).invoke(ctx)
      File "/home/myaccount/.local/lib/python3.10/site-packages/click/core.py", line 1404, in invoke
        return ctx.invoke(self.callback, **ctx.params)
      File "/home/myaccount/.local/lib/python3.10/site-packages/click/core.py", line 760, in invoke
        return __callback(*args, **kwargs)
      File "/home/myaccount/tools/Spray365/modules/spray/spray.py", line 159, in command
        auth_result = helpers.authenticate_credential(cred, proxy, insecure)
      File "/home/myaccount/tools/Spray365/modules/spray/helpers.py", line 52, in authenticate_credential
        raw_result = auth_app.acquire_token_by_username_password(
      File "/home/myaccount/.local/lib/python3.10/site-packages/msal/application.py", line 1420, in acquire_token_by_username_password
        response = _clean_up(self._acquire_token_by_username_password_federated(
      File "/home/myaccount/.local/lib/python3.10/site-packages/msal/application.py", line 1447, in _acquire_token_by_username_password_federated
        wstrust_result = wst_send_request(
      File "/home/myaccount/.local/lib/python3.10/site-packages/msal/wstrust_request.py", line 60, in send_request
        return parse_response(resp.text)
      File "/home/myaccount/.local/lib/python3.10/site-packages/msal/wstrust_response.py", line 49, in parse_response
        raise RuntimeError("WsTrust server returned error in RSTR: %s" % (error or body))
    RuntimeError: WsTrust server returned error in RSTR: {'reason': 'ID3242: The security token could not be authenticated or authorized.', 'code': 'a:FailedAuthentication'}
    [2022-08-05 06:37:16 - INFO]: Authentication results saved to file 'spray365_results_2022-08-05_06-37-16.json'
    

    It seems the msal library has been modified. I think authentication now requires a UPN, an not a username only.

    Best regard,

    opened by quentinhardy 1
  • An exception was raised: ValueError

    An exception was raised: ValueError

    Have used this tool successfully before. Recently tried to run a spray after creating an execution plan like normal. However when running sudo python3 spray365.py spray -ep spray_ep -l 2 the tool seems to load fine but errors out after making one request An exception was raised: Value Error

    Have tried running in two kali env's (Bare metal & VM) but same error.

    image

    opened by mbb5546 2
Releases(0.2.2-beta)
  • 0.2.2-beta(Jul 14, 2022)

    ⚠️ Notice

    When upgrading from a previous version of Spray365, ensure that you install the latest dependencies! Install the latest version of Spray365 and its dependencies with:

    $ git clone https://github.com/MarkoH17/Spray365
    $ cd Spray365
    ~/Spray365$ pip3 install -r requirements.txt -U
    ~/Spray365$ python3 spray365.py
    

    🎉 What's New

    • Review module now shows which AAD endpoints are accessible (e.g., not protected by MFA / conditional access policies)

    🏗️ What's Changed

    • Exceptions are now displayed in more detail to aid in debugging
    • Code cleanup (import re-ordering)

    Full Changelog: https://github.com/MarkoH17/Spray365/compare/0.2.1-beta...0.2.2-beta

    Source code(tar.gz)
    Source code(zip)
  • 0.2.1-beta(Jun 1, 2022)

    ⚠️ Notice

    When upgrading from a previous version of Spray365, ensure that you install the latest dependencies! Install the latest version of Spray365 and its dependencies with:

    $ git clone https://github.com/MarkoH17/Spray365
    $ cd Spray365
    ~/Spray365$ pip3 install -r requirements.txt -U
    ~/Spray365$ python3 spray365.py
    

    🎉 What's New

    • Version checking warns users when running Spray365 with older versions of Python
    • Added PEP 563 (Postponed Evaluation of Annotations) to enable the use of modern type hints (3.10+) on older versions of Python (3.7+)

    🏗️ What's Changed

    • Passwords supplied in the userfile may now contain a colon
    • Execution plans are properly saved when generated in audit mode

    Full Changelog: https://github.com/MarkoH17/Spray365/compare/0.2.0-beta...0.2.1-beta

    Source code(tar.gz)
    Source code(zip)
  • 0.2.0-beta(May 22, 2022)

    ⚠️ Notice

    When upgrading from a previous version of Spray365, ensure that you install the latest dependencies! Install the latest version of Spray365 and its dependencies with:

    $ git clone https://github.com/MarkoH17/Spray365
    $ cd Spray365
    ~/Spray365$ pip3 install -r requirements.txt -U
    ~/Spray365$ python3 spray365.py
    

    🎉 What's New

    • Spray365 has been refactored into different modules, making it easier to develop new features!
    • Generating execution plans have been split into two different sub-types: normal, and audit. Generating normal execution plans (spray365.py generate normal) works the same as previous versions. However, generating audit execution plans (spray365.py generate audit) will cause Spray365 to create all combinations of User-Agent, AAD_Client, and AAD_Endpoint for each set of credentials. Audit-type execution plans can (and should) be sprayed with known-valid credentials to identify flaws in MFA / conditional access policy configurations.
    • Audit-type execution plans inspired the addition of another type of password input: --passwords_in_userfile. When this option is specified, each line in the user file (-u / --user_file) will be split on a colon, where the value preceding the colon is treated as the username, and the value after the colon is treated as the password (e.g. jsmith:Password01).
    • Results from spraying can be reviewed using the new review mode (spray365.py review <results.json>). This new mode displays valid/invalid users and valid/partial-valid/invalid credentials.

    🏗️ What's Changed

    • Arguments are now parsed using Click, simplifying validation and enforcement of mutually exclusive options.
    • Credential, AuthResult, and AuthError objects were refactored. This means that only new execution plans (and Spray365 results) can be processed using v0.2.0-beta.
    • Dependencies were bumped to their latest versions.

    Full Changelog: https://github.com/MarkoH17/Spray365/compare/0.1.5-beta...0.2.0-beta

    Source code(tar.gz)
    Source code(zip)
  • 0.1.5-beta(Jan 24, 2022)

    What's New 🎉

    • Spray365 is now split into two modes (generate and spray) for more logical argument handling. This is a breaking change that requires Spray365 to be executed using new syntax.

      Example comparing new and old for generating an execution plan
      Old Syntax: spray365.py -g demo.s365 -d <domain> -u <username_file> -pf <password_file> --delay 10
      New Syntax: spray365.py generate --execution_plan demo.s365 -d <domain> -u <username_file> -pf <password_file> --delay 10

      Example comparing new and old for password spraying:
      Old Syntax: spray365.py -s demo.s365 --lockout 3
      New Syntax: spray365.py spray --execution_plan demo.s365 --lockout 3

    • Successive authentication attempts for a given user with known good credentials will be skipped. This new default behavior can be reverted with the -i / --ignore_success argument in spray mode.

    What's Changed

    • An authentication request requiring interactive login (AAD error "AADSTS65001") is now considered a successful login
    • Help documentation improved and updated to match Spray365 capabilities

    Full Changelog: https://github.com/MarkoH17/Spray365/compare/0.1.4-beta...0.1.5-beta

    Source code(tar.gz)
    Source code(zip)
  • 0.1.4-beta(Jan 4, 2022)

    What's New 🎉

    • N/A

    What's Changed

    • Successful login no longer causes a fatal error
    • An authentication request for a nonexistent application (client id) returns AAD error "AADSTS700016", and is now considered a successful login

    Full Changelog: https://github.com/MarkoH17/Spray365/compare/0.1.3-beta...0.1.4-beta

    Source code(tar.gz)
    Source code(zip)
  • 0.1.3-beta(Dec 8, 2021)

    What's New 🎉

    • Added behavior to ensure that results from password spraying are saved on exit (even when an exception is thrown or the application is interrupted)

    What's Changed

    • Duplicate usernames are removed before generating an execution plan
    • Better error handling when parsing a malformed execution plan

    Full Changelog: https://github.com/MarkoH17/Spray365/compare/0.1.2-beta...0.1.3-beta

    Source code(tar.gz)
    Source code(zip)
  • 0.1.2-beta(Nov 23, 2021)

    What's New 🎉

    • Added -k, --insecure argument to ignore TLS errors, which is useful when proxying traffic (@fang0654)
    • Added urllib3 (>= 1.26.5) dependency to requirements file

    What's Changed

    • Usernames read from file have whitespace stripped before their usage
    • Fixed resume index usage so that -R RESUME_INDEX, --resume_index RESUME_INDEX argument will resume spraying from the specified index in the execution file (1-based index), instead of starting at the credential prior to the targeted index (an off-by-one error)
    • Requirements file is more relaxed

    Full Changelog: https://github.com/MarkoH17/Spray365/compare/0.1.1-beta...0.1.2-beta

    Source code(tar.gz)
    Source code(zip)
  • 0.1.1-beta(Nov 11, 2021)

    • Support for rotating or using a custom user agent during authentication requests
    • Better enforcement of the account lockout threshold argument
    • Detection of more relevant authentication-related failures
    • More useful help text
    • Code cleanup

    This release likely contains bugs and other unwanted behavior.

    Source code(tar.gz)
    Source code(zip)
  • 0.1.0-beta(Nov 6, 2021)

Owner
Mark Hedrick
I'm an offensive security enthusiast with years of software development experience in Microsoft .NET, Python, and C.
Mark Hedrick
Abusing Microsoft 365 OAuth Authorization Flow for Phishing Attack

O365DevicePhish Microsoft365_devicePhish Abusing Microsoft 365 OAuth Authorization Flow for Phishing Attack This is a simple proof-of-concept script t

Trewis [work] Scotch 4 Sep 23, 2022
Oh365UserFinder is used for identifying valid o365 accounts without the risk of account lockouts.

Oh365 User Finder Oh365UserFinder is used for identifying valid o365 accounts without the risk of account lockouts. The tool parses responses to ident

Joe Helle 414 Jan 2, 2023
Having a weak password is not good for a system that demands high confidentiality and security of user credentials

Having a weak password is not good for a system that demands high confidentiality and security of user credentials. It turns out that people find it difficult to make up a strong password that is strong enough to prevent unauthorized users from memorizing it.

PyLaboratory 0 Feb 7, 2022
This is a multi-password‌ cracking tool that can help you hack facebook accounts very quickly

Pro_Crack Facebook Fast Cracking Tool This is a multi-password‌ cracking tool that can help you hack facebook accounts very quickly Installation On Te

•JINN• 1 Jan 16, 2022
Now patched 0day for force reseting an accounts password

Animal Jam 0day No-Auth Force Password Reset via API Now patched 0day for force reseting an accounts password Used until patched to cause anarchy. Pro

IRIS 10 Nov 17, 2022
Guess the password for Tik Tok accounts

Guess the password for Tik Tok accounts Tool features : You don't need proxies There is no captcha Running on a private api Combo T

null 32 Dec 25, 2022
Brute Force Guess the password for Instgram accounts with python

Brute-Force-instagram Guess the password for Instgram accounts Tool features : It has two modes: 1- Combo system from you 2- Automatic (random) system

null 45 Dec 11, 2022
A python base script from which you can hack or clone any person's facebook friendlist or followers accounts which have simple password

Hcoder This is a python base script from which you can hack or clone any person's facebook friendlist or followers accounts which have simple password

Muhammad Hamza 3 Dec 6, 2021
CamRaptor is a tool that exploits several vulnerabilities in popular DVR cameras to obtain device credentials.

CamRaptor is a tool that exploits several vulnerabilities in popular DVR cameras to obtain device credentials.

EntySec 118 Dec 24, 2022
zip-brute Zip File Password Cracking with Using Password List

Zip brute is a python script that cracks zip that are password protected using a wordlist dictionary.

AnonyminHack5 13 Nov 3, 2022
A tool to brute force a gmail account. Use this tool to crack multiple accounts

A tool to brute force a gmail account. Use this tool to crack multiple accounts. This tool is developed to crack multiple accounts

Saad 12 Dec 30, 2022
Python library to remotely extract credentials on a set of hosts.

Python library to remotely extract credentials on a set of hosts.

Pixis 1.5k Dec 31, 2022
Infoga is a tool gathering email accounts informations (ip,hostname,country,...) from different public source

Infoga - Email OSINT Infoga is a tool gathering email accounts informations (ip,hostname,country,...) from different public source (search engines, pg

m4ll0k (mallok) 1.8k Jan 4, 2023
CamOver is a camera exploitation tool that allows to disclosure network camera admin password.

CamOver is a camera exploitation tool that allows to disclosure network camera admin password. Features Exploits vulnerabilities in most popul

EntySec 247 Jan 2, 2023
This is a Cryptographied Password Manager, a tool for storing Passwords in a Secure way

Cryptographied Password Manager This is a Cryptographied Password Manager, a tool for storing Passwords in a Secure way without using external Service

Francesco 3 Nov 23, 2022
A tool to crack a wifi password with a help of wordlist

A tool to crack a wifi password with a help of wordlist. This may take long to crack a wifi depending upon number of passwords your wordlist contains. Also it is slower as compared to social media accounts cracking. I've made enough efforts to make it as fast as possible

Saad 144 Dec 29, 2022
Microsoft Exchange Server SSRF漏洞(CVE-2021-26855)

Microsoft_Exchange_Server_SSRF_CVE-2021-26855 zoomeye dork:app:"Microsoft Exchange Server" 使用Seebug工具箱及pocsuite3编写的脚本Microsoft_Exchange_Server_SSRF_CV

conjojo 37 Nov 12, 2022
Gmail Accounts Hacking

gmail-hack Gmail Accounts Hacking Gemail-Hack python script for Hack gmail account brute force What is brute force attack? In brute force attack,scrip

Aryan 25 Nov 10, 2022
A python script to brute-force guess the passwords to Instagram accounts

Instagram-Brute-Force The purpose of this script is to brute-force guess the passwords to Instagram accounts. Specifics: Comes with 2 separate modes i

Moondog 2 Nov 16, 2021