Skip to content

Commit

Permalink
bug fixes for o365enum
Browse files Browse the repository at this point in the history
  • Loading branch information
knavesec committed Jan 21, 2022
1 parent 4bba808 commit 529e5e7
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 11 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ The following plugins are currently supported:
* [OWA](https://github.com/knavesec/CredMaster/wiki/OWA) - Outlook Web Access
* [EWS](https://github.com/knavesec/CredMaster/wiki/EWS) - Exchange Web Services
* [O365](https://github.com/knavesec/CredMaster/wiki/O365) - Office365
* [O364Enum](https://github.com/knavesec/CredMaster/wiki/O365Enum) - Office365 User Enum (No Authentication Request)
* [O365Enum](https://github.com/knavesec/CredMaster/wiki/O365Enum) - Office365 User Enum (No Authentication Request)
* [MSOL](https://github.com/knavesec/CredMaster/wiki/MSOL) - Microsoft Online
* [Okta](https://github.com/knavesec/CredMaster/wiki/Okta) - Okta Authentication Portal
* [FortinetVPN](https://github.com/knavesec/CredMaster/wiki/FortinetVPN) - Fortinet VPN Client
Expand All @@ -57,7 +57,6 @@ PRs welcome :)
* "Resume" functionality for paused/cancelled scans. Ideally storing data for APIs used, if they were destroyed and what user/pwd the spray was on
* Password file read dynamically so you can add/delete pwds mid-scan and it will perform as desired
* Method to reliably determine if an auth attempt was throttled, so the username could be re-queued and tried again later for full cover (would have to be per-plugin, return "throttled" boolean value in plugin script, requeue if throttled)
* Method to add a custom header to each attempt as a "marker" for attribution if desired


## Credits ##
Expand All @@ -76,5 +75,5 @@ PRs welcome :)


Feel free to drop me a line
[keybase] - \@knave
[twitter] - [knavesec](https://twitter.com/knavesec)
- \@knave on Keybase
[Twitter - knavesec](https://twitter.com/knavesec)
50 changes: 44 additions & 6 deletions credmaster.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ def main(args,pargs):
passwordsperdelay = args.passwordsperdelay
jitter = args.jitter
jitter_min = args.jitter_min
randomize = args.randomize
headers = args.header


# input exception handling
if outfile != None:
Expand Down Expand Up @@ -116,6 +119,12 @@ def main(args,pargs):
connect_success, testconnect_output, pluginargs = validator.testconnect(pluginargs, args, apis['us-east-2'], useragent)
log_entry(testconnect_output)

if headers is not None:
log_entry("Adding custom header \"{}\" to requests".format(headers))
head = headers.split(":")[0].strip()
val = headers.split(":")[1].strip()
pluginargs["custom-headers"] = {head : val}

if not connect_success:
destroy_apis(apis, access_key, secret_access_key, profile_name, session_token)
return
Expand All @@ -132,7 +141,7 @@ def main(args,pargs):

for password in passwords:

load_credentials(username_file, password, useragent_file, userpass=userpass_file)
load_credentials(username_file, password, useragent_file, userpass=userpass_file, randomize=randomize)

# Start Spray
threads = []
Expand Down Expand Up @@ -338,6 +347,9 @@ def spray_thread(api_key, api_dict, plugin, pluginargs, jitter=None, jitter_min=

response = plugin_authentiate(api_dict['proxy_url'], cred['username'], cred['password'], cred['useragent'], pluginargs)

if "debug" in response.keys():
print(response["debug"])

if not response['error']:
log_entry("{}: {}".format(api_key,response['output']))
else:
Expand All @@ -351,14 +363,18 @@ def spray_thread(api_key, api_dict, plugin, pluginargs, jitter=None, jitter_min=
log_entry("ERROR: {}: {} - {}".format(api_key,cred['username'],ex))


def load_credentials(user_file, password, useragent_file=None, userpass=None):
def load_credentials(user_file, password, useragent_file=None, userpass=None, randomize=False):

r = ""
if randomize:
r = ", randomized order"

users = []
if userpass is None:
log_entry('Loading credentials from {} with password {}'.format(user_file, password))
log_entry('Loading credentials from {} with password {}{}'.format(user_file, password, r))
users = load_file(user_file)
else:
log_entry('Loading credentials from {} as user-pass file'.format(userpass))
log_entry('Loading credentials from {} as user-pass file{}'.format(userpass, r))
users = load_file(userpass)


Expand All @@ -368,10 +384,18 @@ def load_credentials(user_file, password, useragent_file=None, userpass=None):
# randomly selected
useragents = ["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0"]

for user in users:
while users != []:
user = None

if randomize:
user = users.pop(random.randint(0,len(users)-1))
else:
user = users.pop(0)

if userpass != None:
password = ":".join(user.split(':')[1:]).strip()
user = user.split(':')[0].strip()
username = user.split(':')[0].strip()

cred = {}
cred['username'] = user
cred['password'] = password
Expand All @@ -380,6 +404,18 @@ def load_credentials(user_file, password, useragent_file=None, userpass=None):
q_spray.put(cred)


# for user in users:
# if userpass != None:
# password = ":".join(user.split(':')[1:]).strip()
# user = user.split(':')[0].strip()
# cred = {}
# cred['username'] = user
# cred['password'] = password
# cred['useragent'] = random.choice(useragents)
#
# q_spray.put(cred)


def load_file(filename):

if filename:
Expand Down Expand Up @@ -417,6 +453,8 @@ def log_entry(entry):
parser.add_argument('-j', '--jitter', type=int, default=None, required=False, help='Jitter delay between requests in seconds (applies per-thread)')
parser.add_argument('-m', '--jitter_min', type=int, default=None, required=False, help='Minimum jitter time in seconds, defaults to 0')
parser.add_argument('-d', '--delay', type=int, required=False, help='Delay between unique passwords, in minutes')
parser.add_argument('-r', '--randomize', required=False, action="store_true", help='Randomize the input list of usernames to spray (will remain the same password)')
parser.add_argument('--header', default=None, required=False, help='Add a custom header to each request for attribution, specify "X-Header: value"')
parser.add_argument('--passwordsperdelay', type=int, default=1, required=False, help='Number of passwords to be tested per delay cycle')
parser.add_argument('--profile_name', type=str, default=None, help='AWS Profile Name to store/retrieve credentials')
parser.add_argument('--access_key', type=str, default=None, help='AWS Access Key')
Expand Down
6 changes: 5 additions & 1 deletion plugins/o365enum/o365enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ def o365enum_authenticate(url, username, password, useragent, pluginargs):
"X-My-X-Amzn-Trace-Id" : trace_id,
}

if "custom-headers" in pluginargs.keys():
for header in pluginargs["custom-headers"]:
headers[header] = pluginargs["custom-headers"][header]

try:

# some code stolen from:
Expand All @@ -50,7 +54,7 @@ def o365enum_authenticate(url, username, password, useragent, pluginargs):

sess = requests.session()

response = sess.post("{}/common/GetCredentialType".format(url), data=body)
response = sess.post("{}/common/GetCredentialType".format(url), headers=headers, data=body)

throttle_status = int(response.json()['ThrottleStatus'])
if_exists_result = str(response.json()['IfExistsResult'])
Expand Down
5 changes: 5 additions & 0 deletions utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ def generate_trace_id():
return str + first + "-" + second


def generate_string(chars):

return "".join(random.choice("0123456789abcdefghijklmnopqrstuvwxyz") for _ in range(chars))


def get_owa_domain(url, uri, useragent):
# Stolen from https://github.com/byt3bl33d3r/SprayingToolkit who stole it from https://github.com/dafthack/MailSniper
auth_header = {
Expand Down

0 comments on commit 529e5e7

Please sign in to comment.