Skip to content

Commit

Permalink
Improvements to URL handling
Browse files Browse the repository at this point in the history
Signed-off-by: Sachin Kamath <[email protected]>
  • Loading branch information
pwnfoo committed Dec 12, 2019
1 parent 4bdac19 commit 151551e
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 20 deletions.
22 changes: 10 additions & 12 deletions src/ntlmrecon/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from colorama import init as init_colorama
from multiprocessing.dummy import Pool as ThreadPool
from ntlmrecon.ntlmutil import gather_ntlm_info
from ntlmrecon.ntlmutil import gather_ntlm_info, url_is_reachable
from ntlmrecon.misc import print_banner, wordlist
from ntlmrecon.inpututils import readfile_and_gen_input, read_input_and_gen_list
from termcolor import colored
Expand Down Expand Up @@ -53,7 +53,6 @@ def write_records_to_csv(records, filename):


def main():

# Init arg parser
parser = argparse.ArgumentParser(description=print_banner())
group = parser.add_mutually_exclusive_group()
Expand All @@ -79,24 +78,23 @@ def main():
print(colored("[!] File already exists. Please choose a different file name", "red"))
sys.exit()

pool = ThreadPool(args.threads)
pool = ThreadPool(int(args.threads))

if args.input:
if args.shuffle:
records = read_input_and_gen_list(args.input, shuffle=True)
else:
records = read_input_and_gen_list(args.input, shuffle=False)
records = read_input_and_gen_list(args.input, shuffle=args.shuffle)
elif args.infile:
if args.shuffle:
records = readfile_and_gen_input(args.infile, shuffle=True)
else:
records = readfile_and_gen_input(args.infile, shuffle=False)
records = readfile_and_gen_input(args.infile, shuffle=args.shuffle)
else:
sys.exit(1)

# Identify all URLs with web servers running
print(colored('[+] Identifying all endpoints with web servers..', 'green'))
for record in records:
print(colored("[+] Brute-forcing {} endpoints on {}".format(len(wordlist), record), "yellow"))
all_combos = []
for word in wordlist:
# TODO : Dirty now, do sanity checks
all_combos.append(record+word)
all_combos.append(str(record+word))
results = pool.map(gather_ntlm_info, all_combos)
results = [x for x in results if x]
write_records_to_csv(results, args.outfile)
Expand Down
1 change: 0 additions & 1 deletion src/ntlmrecon/inpututils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
"([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$"



def _cidr_to_iplist(cidr):
try:
ip_range = IpRangeList(cidr)
Expand Down
39 changes: 32 additions & 7 deletions src/ntlmrecon/ntlmutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,22 @@
import collections
from random import choice
import json
from termcolor import colored
from colorama import init

init()


# We are hackers. SSL warnings don't stop us, although this is not recommended.
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


# Decoder taken from https://gist.github.com/aseering/829a2270b72345a1dc42 , ported and modified
# Decoder taken from https://gist.github.com/aseering/829a2270b72345a1dc42, Python3 ported and modified
VALID_CHRS = set(string.ascii_letters + string.digits + string.punctuation)

FOUND_DOMAINS = ['google.com']

FAIL_DOMAINS = []

def clean_str(st):
return ''.join((s if s in VALID_CHRS else '?') for s in st)

Expand Down Expand Up @@ -146,23 +150,44 @@ def is_valid_url(url):
return False


def url_is_reachable(url):
try:
response = requests.head(url, verify=False)
except (ConnectionError, OSError):
print("[!] No web server present at {}".format(url))
return False
else:
return url
# Verifies if the endpoint has authentication enabled and looks for NTLM specifically


def detect_ntlm_auth(url):
global FAIL_DOMAINS

if not is_valid_url(url):
return False
else:
try:
response = requests.head(url, verify=False, timeout=4)
if urlparse(url).netloc in FAIL_DOMAINS:
return False
else:
response = requests.head(url, verify=False, timeout=3)
except (OSError, ConnectionError) as e:
if urlparse(url).netloc not in FAIL_DOMAINS:
FAIL_DOMAINS.append(urlparse(url).netloc)

return False

except Exception as e:
print('[!] Error processing {} - '.format(url), e.__class__.__name__)
return "FAIL " + str(e.__class__.__name__)
return False

else:
if response.status_code == 401:
response_headers = dict(response.headers)
if 'WWW-Authenticate' in response_headers.keys():
if 'NTLM' in response_headers['WWW-Authenticate']:
print("[+] {} has NTLM authentication enabled".format(url))
print(colored("[+] {} has NTLM authentication enabled!".format(url), 'green'))
return True
else:
print("[+] {} requires authentication but the method was found to be {}".format(
Expand All @@ -182,6 +207,7 @@ def gather_ntlm_info(url):
response_data[url]['meta'] = dict()

ntlm_check_response = detect_ntlm_auth(url)

if ntlm_check_response:
if type(ntlm_check_response) is not bool:
if 'FAIL ' in ntlm_check_response:
Expand Down Expand Up @@ -224,15 +250,14 @@ def gather_ntlm_info(url):
return False

else:
return False
"""
response_data[url]['meta']['has_authenticate_header'] = False
response_data[url]['meta']['status'] = 'fail'
response_data[url]['meta']['reason'] = 'WWW-Authenticate header not found. Headers - {}'.format(
str(auth_header))
return response_data
"""
return False

else:
print("[!] No NTLM authentication endpoint found at {}".format(url))
return False

0 comments on commit 151551e

Please sign in to comment.