Commit 367e64a2 authored by Nong Hoang Tu's avatar Nong Hoang Tu
Browse files

New upstream version 0.4

parents
Pipeline #3234 failed with stages
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. Kali Linux]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
name: Upload Python Package
on:
release:
types: [created]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# Pycharm stuff
.idea/
MIT License
Copyright (c) 2019 Sachin S. Kamath (@sachinkamath)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://GitHub.com/sachinkamath/ntlmrecon/graphs/commit-activity)
# NTLMRecon
A fast and flexible NTLM reconnaissance tool without external dependencies. Useful to find out information about NTLM endpoints when working with a large set of potential IP addresses and domains.
NTLMRecon is built with flexibilty in mind. Need to run recon on a single URL, an IP address, an entire CIDR range or combination of all of it all put in a single input file? No problem! NTLMRecon got you covered. Read on.
# Demo
[![asciicast](https://asciinema.org/a/e4ggPBbzpJj9cIWRwK67D8xnw.svg)](https://asciinema.org/a/e4ggPBbzpJj9cIWRwK67D8xnw)
# TODO
1. Implement aiohttp based solution for sending requests
2. Integrate a spraying library
3. Add other authentication schemes found to the output
4. Automatic detection of autodiscover domains if domain
# Overview
NTLMRecon looks for NTLM enabled web endpoints, sends a fake authentication request and enumerates the following information from the NTLMSSP response:
1. AD Domain Name
2. Server name
3. DNS Domain Name
4. FQDN
5. Parent DNS Domain
Since NTLMRecon leverages a python implementation of NTLMSSP, it eliminates the overhead of running Nmap NSE `http-ntlm-info` for every successful discovery.
On every successful discovery of a NTLM enabled web endpoint, the tool enumerates and saves information about the domain as follows to a CSV file :
| URL | Domain Name | Server Name | DNS Domain Name | FQDN | DNS Domain |
|-------------------------- |------------- |------------- |------------------- |------------------------------ |------------- |
| https://contoso.com/EWS/ | XCORP | EXCHANGE01 | xcorp.contoso.net | EXCHANGE01.xcorp.contoso.net | contoso.net |
# Installation
### BlackArch
NTLMRecon is already packaged for BlackArch and can be installed by running `pacman -S ntlmrecon`
### Arch
If you're on Arch Linux or any Arch linux based distribution, you can grab the latest build from the [Arch User Repository](https://aur.archlinux.org/packages/ntlmrecon/).
### Build from source
1. Clone the repository : `git clone https://github.com/sachinkamath/ntlmrecon/`
2. RECOMMENDED - Install virtualenv : `pip install virtualenv`
3. Start a new virtual environment : `virtualenv venv` and activate it with `source venv/bin/activate`
4. Run the setup file : `python setup.py install`
5. Run ntlmrecon : `ntlmrecon --help`
# Usage
<pre>
$ ntlmrecon --help
_ _ _____ _ ___ _________
| \ | |_ _| | | \/ || ___ \
| \| | | | | | | . . || |_/ /___ ___ ___ _ __
| . ` | | | | | | |\/| || // _ \/ __/ _ \| '_ \
| |\ | | | | |____| | | || |\ \ __/ (_| (_) | | | |
\_| \_/ \_/ \_____/\_| |_/\_| \_\___|\___\___/|_| |_| - @pwnfoo
v.0.4 beta - Y'all still exposing NTLM endpoints?
Bug Reports, Feature Requests : https://git.io/JIR5z
usage: ntlmrecon [-h] [--input INPUT | --infile INFILE] [--wordlist WORDLIST]
[--threads THREADS] [--output-type] [--outfile OUTFILE]
[--random-user-agent] [--force-all] [--shuffle] [-f]
optional arguments:
-h, --help show this help message and exit
--input INPUT, -i INPUT
Pass input as an IP address, URL or CIDR to enumerate
NTLM endpoints
--infile INFILE, -I INFILE
Pass input from a local file
--wordlist WORDLIST Override the internal wordlist with a custom wordlist
--threads THREADS Set number of threads (Default: 10)
--output-type, -o Set output type. JSON (TODO) and CSV supported
(Default: CSV)
--outfile OUTFILE, -O OUTFILE
Set output file name (Default: ntlmrecon.csv)
--random-user-agent TODO: Randomize user agents when sending requests
(Default: False)
--force-all Force enumerate all endpoints even if a valid endpoint
is found for a URL (Default : False)
--shuffle Break order of the input files
-f, --force Force replace output file if it already exists
</pre>
## Example Usage
### Recon on a single URL
` $ ntlmrecon --input https://mail.contoso.com --outfile ntlmrecon.csv`
### Recon on a CIDR range or IP address
` $ ntlmrecon --input 192.168.1.1/24 --outfile ntlmrecon-ranges.csv`
### Recon on an input file
The tool automatically detects the type of input per line and gives you results automatically. CIDR ranges are expanded automatically even when read from a text file.
Input file can be something as mixed up as :
<pre>
mail.contoso.com
CONTOSOHOSTNAME
10.0.13.2/28
192.168.222.1/24
https://mail.contoso.com
</pre>
To run recon with an input file, just run :
`$ ntlmrecon --infile /path/to/input/file --outfile ntlmrecon-fromfile.csv`
# Acknowledgements
* [@nyxgeek](https://github.com/nyxgeek) for the idea behind [ntlmscan](https://github.com/nyxgeek/ntlmscan).
# Feedback
If you'd like to see a feature added into the tool or something doesn't work for you, please open a new [issue](https://github.com/sachinkamath/ntlmrecon/issues/new).
/abs
/adfs/services/trust/2005/windowstransport
/aspnet_client/
/Autodiscover
/Autodiscover/AutodiscoverService.svc/root
/Autodiscover/Autodiscover.xml
/AutoUpdate/
/CertEnroll/
/CertProv
/CertSrv/
/Conf/
/debug/
/deviceupdatefiles_ext/
/deviceupdatefiles_int/
/dialin
/ecp/
/Etc/
/EWS/
/Exchange/
/Exchweb/
/GroupExpansion/
/HybridConfig
/iwa/authenticated.aspx
/iwa/iwa_test.aspx
/mcx
/meet
/Microsoft-Server-ActiveSync/
/OAB/
/ocsp/
/owa/
/PersistentChat
/PhoneConferencing/
/PowerShell/
/Public/
/Reach/sip.svc
/RequestHandler/
/RequestHandlerExt
/RequestHandlerExt/
/Rgs/
/RgsClients
/Rpc/
/RpcWithCert/
/scheduler
/Ucwa
/UnifiedMessaging/
/WebTicket
/WebTicket/WebTicketService.svc
/_windows/default.aspx?ReturnUrl=/
from setuptools import setup, find_packages
from os import path
here = path.abspath(path.dirname(__file__))
with open(path.join(here, 'README.md'), encoding='utf-8') as f:
long_description = f.read()
setup(
name='ntlmrecon', # Required
version='0.4b0', # Required
description='A tool to enumerate information from NTLM authentication enabled web endpoints', # Optional
long_description=long_description, # Optional
long_description_content_type='text/markdown', # Optional (see note above)
url='https://github.com/sachinkamath/ntlmrecon', # Optional
# This should be your name or the name of the organization which owns the
# project.
author='Sachin S Kamath (@sachinkamath)', # Optional
# This should be a valid email address corresponding to the author listed
# above.
author_email='mail@skamath.me', # Optional
keywords='security recon redteam cybersecurity ntlm ntlmrecon', # Optional
package_dir={'': 'src'},
packages=find_packages(where='src'), # Required
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4',
install_requires=['requests', 'colorama', 'termcolor', 'iptools'], # TODO
# For example, the following would provide a command called `sample` which
# executes the function `main` from this package when invoked:
entry_points={ # Optional
'console_scripts': [
'ntlmrecon=ntlmrecon:main',
],
},
project_urls={ # Optional
'Bug Reports': 'https://github.com/sachinkamath/ntlmrecon/issues',
'Source': 'https://github.com/sachinkamath/ntlmrecon/',
},
)
import argparse
import json
import requests
import csv
import sys
import os
from colorama import init as init_colorama
from multiprocessing.dummy import Pool as ThreadPool
from ntlmrecon.ntlmutil import gather_ntlm_info
from ntlmrecon.misc import print_banner, INTERNAL_WORDLIST
from ntlmrecon.inpututils import readfile_and_gen_input, read_input_and_gen_list
from termcolor import colored
from urllib.parse import urlsplit
# Initialize colors in Windows - Because I like Windows too!
init_colorama()
# make the Pool of workers
# TODO: Make this an argument
FOUND_DOMAINS = []
def in_found_domains(url):
split_url = urlsplit(url)
if split_url.hostname in FOUND_DOMAINS:
return True
else:
return False
def write_records_to_csv(records, filename):
if os.path.exists(filename):
append_write = 'a'
else:
append_write = 'w+'
with open(filename, append_write) as file:
writer = csv.writer(file)
if append_write == 'w+':
writer.writerow(['URL', 'AD Domain Name', 'Server Name', 'DNS Domain Name', 'FQDN', 'Parent DNS Domain'])
for record in records:
csv_record = list()
url = list(record.keys())[0]
csv_record.append(url)
csv_record.extend(list(record[url]['data'].values()))
writer.writerow(csv_record)
def main():
# Init arg parser
parser = argparse.ArgumentParser(description=print_banner())
group = parser.add_mutually_exclusive_group()
group.add_argument('--input', '-i', help='Pass input as an IP address, URL or CIDR to enumerate NTLM endpoints')
group.add_argument('--infile', '-I', help='Pass input from a local file')
parser.add_argument('--wordlist', help='Override the internal wordlist with a custom wordlist', required=False)
parser.add_argument('--threads', help="Set number of threads (Default: 10)", required=False, default=10)
parser.add_argument('--output-type', '-o', help='Set output type. JSON (TODO) and CSV supported (Default: CSV)',
required=False, default='csv', action="store_true")
parser.add_argument('--outfile', '-O', help='Set output file name (Default: ntlmrecon.csv)', default='ntlmrecon.csv')
parser.add_argument('--random-user-agent', help="TODO: Randomize user agents when sending requests (Default: False)",
default=False, action="store_true")
parser.add_argument('--force-all', help="Force enumerate all endpoints even if a valid endpoint is found for a URL "
"(Default : False)", default=False, action="store_true")
parser.add_argument('--shuffle', help="Break order of the input files", default=False, action="store_true")
parser.add_argument('-f', '--force', help="Force replace output file if it already exists", action="store_true",
default=False)
args = parser.parse_args()
if not args.input and not args.infile:
print(colored("[!] How about you check the -h flag?", "red"))
if os.path.isdir(args.outfile):
print(colored("[!] Invalid filename. Please enter a valid filename!", "red"))
sys.exit()
elif os.path.exists(args.outfile) and not args.force:
print(colored("[!] Output file {} already exists. "
"Choose a different file name or use -f to overwrite the file".format(args.outfile), "red"))
sys.exit()
pool = ThreadPool(int(args.threads))
if args.input:
records = read_input_and_gen_list(args.input, shuffle=args.shuffle)
elif args.infile:
records = readfile_and_gen_input(args.infile, shuffle=args.shuffle)
else:
sys.exit(1)
# Check if a custom wordlist is specified
if args.wordlist:
try:
with open(args.wordlist, 'r') as fr:
wordlist = fr.read().split('\n')
wordlist = [x for x in wordlist if x]
except (OSError, FileNotFoundError):
print(colored("[!] Cannot read the specified file {}. Check if file exists and you have "
"permission to read it".format(args.wordlist), "red"))
sys.exit(1)
else:
wordlist = INTERNAL_WORDLIST
# Identify all URLs with web servers running
for record in records:
print(colored("[+] Brute-forcing {} endpoints on {}".format(len(wordlist), record), "yellow"))
all_combos = []
for word in wordlist:
if word.startswith('/'):
all_combos.append(str(record+word))
else:
all_combos.append(str(record+"/"+word))
results = pool.map(gather_ntlm_info, all_combos)
results = [x for x in results if x]
if results:
write_records_to_csv(results, args.outfile)
print(colored('[+] Output for {} saved to {} '.format(record, args.outfile), 'green'))
from iptools import IpRangeList
import sys
import re
import random
CIDR_REGEX = "^([0-9]{1,3}\.){3}[0-9]{1,3}(\/([0-9]|[1-2][0-9]|3[0-2]))?$"
URL_REGEX = "^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$"
HOST_REGEX = "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*" \
"([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)
return list(ip_range)
except TypeError:
print("[!] That's not a valid IP address or CIDR")
return False
def _identify_and_return_records(inputstr, shuffle=False):
master_records = []
if re.match(CIDR_REGEX, inputstr):
<