AutoRecon
This commit is contained in:
parent
15f45be9d5
commit
f7ee16c641
829
autorecon.py
Normal file
829
autorecon.py
Normal file
@ -0,0 +1,829 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# AutoRecon is a multi-threaded network reconnaissance tool which performs automated enumeration of services.
|
||||||
|
#
|
||||||
|
# This program can be redistributed and/or modified under the terms of the
|
||||||
|
# GNU General Public License, either version 3 of the License, or (at your
|
||||||
|
# option) any later version.
|
||||||
|
#
|
||||||
|
|
||||||
|
import atexit
|
||||||
|
import argparse
|
||||||
|
import asyncio
|
||||||
|
import colorama
|
||||||
|
from colorama import Fore, Style
|
||||||
|
from concurrent.futures import ProcessPoolExecutor, as_completed, FIRST_COMPLETED
|
||||||
|
from datetime import datetime
|
||||||
|
import ipaddress
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import socket
|
||||||
|
import string
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import toml
|
||||||
|
import termios
|
||||||
|
|
||||||
|
def _quit():
|
||||||
|
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, TERM_FLAGS)
|
||||||
|
|
||||||
|
atexit.register(_quit)
|
||||||
|
|
||||||
|
TERM_FLAGS = termios.tcgetattr(sys.stdin.fileno())
|
||||||
|
|
||||||
|
verbose = 0
|
||||||
|
nmap = '-vv --reason -Pn'
|
||||||
|
srvname = ''
|
||||||
|
heartbeat_interval = 60
|
||||||
|
port_scan_profile = None
|
||||||
|
|
||||||
|
port_scan_profiles_config = None
|
||||||
|
service_scans_config = None
|
||||||
|
global_patterns = []
|
||||||
|
|
||||||
|
username_wordlist = '/usr/share/seclists/Usernames/top-usernames-shortlist.txt'
|
||||||
|
password_wordlist = '/usr/share/seclists/Passwords/darkweb2017-top100.txt'
|
||||||
|
|
||||||
|
rootdir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
|
single_target = False
|
||||||
|
only_scans_dir = False
|
||||||
|
|
||||||
|
def e(*args, frame_index=1, **kvargs):
|
||||||
|
frame = sys._getframe(frame_index)
|
||||||
|
|
||||||
|
vals = {}
|
||||||
|
|
||||||
|
vals.update(frame.f_globals)
|
||||||
|
vals.update(frame.f_locals)
|
||||||
|
vals.update(kvargs)
|
||||||
|
|
||||||
|
return string.Formatter().vformat(' '.join(args), args, vals)
|
||||||
|
|
||||||
|
def cprint(*args, color=Fore.RESET, char='*', sep=' ', end='\n', frame_index=1, file=sys.stdout, **kvargs):
|
||||||
|
frame = sys._getframe(frame_index)
|
||||||
|
|
||||||
|
vals = {
|
||||||
|
'bgreen': Fore.GREEN + Style.BRIGHT,
|
||||||
|
'bred': Fore.RED + Style.BRIGHT,
|
||||||
|
'bblue': Fore.BLUE + Style.BRIGHT,
|
||||||
|
'byellow': Fore.YELLOW + Style.BRIGHT,
|
||||||
|
'bmagenta': Fore.MAGENTA + Style.BRIGHT,
|
||||||
|
|
||||||
|
'green': Fore.GREEN,
|
||||||
|
'red': Fore.RED,
|
||||||
|
'blue': Fore.BLUE,
|
||||||
|
'yellow': Fore.YELLOW,
|
||||||
|
'magenta': Fore.MAGENTA,
|
||||||
|
|
||||||
|
'bright': Style.BRIGHT,
|
||||||
|
'srst': Style.NORMAL,
|
||||||
|
'crst': Fore.RESET,
|
||||||
|
'rst': Style.NORMAL + Fore.RESET
|
||||||
|
}
|
||||||
|
|
||||||
|
vals.update(frame.f_globals)
|
||||||
|
vals.update(frame.f_locals)
|
||||||
|
vals.update(kvargs)
|
||||||
|
|
||||||
|
unfmt = ''
|
||||||
|
if char is not None:
|
||||||
|
unfmt += color + '[' + Style.BRIGHT + char + Style.NORMAL + ']' + Fore.RESET + sep
|
||||||
|
unfmt += sep.join(args)
|
||||||
|
|
||||||
|
fmted = unfmt
|
||||||
|
|
||||||
|
for attempt in range(10):
|
||||||
|
try:
|
||||||
|
fmted = string.Formatter().vformat(unfmt, args, vals)
|
||||||
|
break
|
||||||
|
except KeyError as err:
|
||||||
|
key = err.args[0]
|
||||||
|
unfmt = unfmt.replace('{' + key + '}', '{{' + key + '}}')
|
||||||
|
|
||||||
|
print(fmted, sep=sep, end=end, file=file)
|
||||||
|
|
||||||
|
def debug(*args, color=Fore.BLUE, sep=' ', end='\n', file=sys.stdout, **kvargs):
|
||||||
|
if verbose >= 2:
|
||||||
|
cprint(*args, color=color, char='-', sep=sep, end=end, file=file, frame_index=2, **kvargs)
|
||||||
|
|
||||||
|
def info(*args, sep=' ', end='\n', file=sys.stdout, **kvargs):
|
||||||
|
cprint(*args, color=Fore.GREEN, char='*', sep=sep, end=end, file=file, frame_index=2, **kvargs)
|
||||||
|
|
||||||
|
def warn(*args, sep=' ', end='\n', file=sys.stderr, **kvargs):
|
||||||
|
cprint(*args, color=Fore.YELLOW, char='!', sep=sep, end=end, file=file, frame_index=2, **kvargs)
|
||||||
|
|
||||||
|
def error(*args, sep=' ', end='\n', file=sys.stderr, **kvargs):
|
||||||
|
cprint(*args, color=Fore.RED, char='!', sep=sep, end=end, file=file, frame_index=2, **kvargs)
|
||||||
|
|
||||||
|
def fail(*args, sep=' ', end='\n', file=sys.stderr, **kvargs):
|
||||||
|
cprint(*args, color=Fore.RED, char='!', sep=sep, end=end, file=file, frame_index=2, **kvargs)
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
def calculate_elapsed_time(start_time):
|
||||||
|
elapsed_seconds = round(time.time() - start_time)
|
||||||
|
|
||||||
|
m, s = divmod(elapsed_seconds, 60)
|
||||||
|
h, m = divmod(m, 60)
|
||||||
|
|
||||||
|
elapsed_time = []
|
||||||
|
if h == 1:
|
||||||
|
elapsed_time.append(str(h) + ' hour')
|
||||||
|
elif h > 1:
|
||||||
|
elapsed_time.append(str(h) + ' hours')
|
||||||
|
|
||||||
|
if m == 1:
|
||||||
|
elapsed_time.append(str(m) + ' minute')
|
||||||
|
elif m > 1:
|
||||||
|
elapsed_time.append(str(m) + ' minutes')
|
||||||
|
|
||||||
|
if s == 1:
|
||||||
|
elapsed_time.append(str(s) + ' second')
|
||||||
|
elif s > 1:
|
||||||
|
elapsed_time.append(str(s) + ' seconds')
|
||||||
|
else:
|
||||||
|
elapsed_time.append('less than a second')
|
||||||
|
|
||||||
|
return ', '.join(elapsed_time)
|
||||||
|
|
||||||
|
port_scan_profiles_config_file = 'port-scan-profiles.toml'
|
||||||
|
with open(os.path.join(rootdir, 'config', port_scan_profiles_config_file), 'r') as p:
|
||||||
|
try:
|
||||||
|
port_scan_profiles_config = toml.load(p)
|
||||||
|
|
||||||
|
if len(port_scan_profiles_config) == 0:
|
||||||
|
fail('There do not appear to be any port scan profiles configured in the {port_scan_profiles_config_file} config file.')
|
||||||
|
|
||||||
|
except toml.decoder.TomlDecodeError as e:
|
||||||
|
fail('Error: Couldn\'t parse {port_scan_profiles_config_file} config file. Check syntax and duplicate tags.')
|
||||||
|
|
||||||
|
with open(os.path.join(rootdir, 'config', 'service-scans.toml'), 'r') as c:
|
||||||
|
try:
|
||||||
|
service_scans_config = toml.load(c)
|
||||||
|
except toml.decoder.TomlDecodeError as e:
|
||||||
|
fail('Error: Couldn\'t parse service-scans.toml config file. Check syntax and duplicate tags.')
|
||||||
|
|
||||||
|
with open(os.path.join(rootdir, 'config', 'global-patterns.toml'), 'r') as p:
|
||||||
|
try:
|
||||||
|
global_patterns = toml.load(p)
|
||||||
|
if 'pattern' in global_patterns:
|
||||||
|
global_patterns = global_patterns['pattern']
|
||||||
|
else:
|
||||||
|
global_patterns = []
|
||||||
|
except toml.decoder.TomlDecodeError as e:
|
||||||
|
fail('Error: Couldn\'t parse global-patterns.toml config file. Check syntax and duplicate tags.')
|
||||||
|
|
||||||
|
if 'username_wordlist' in service_scans_config:
|
||||||
|
if isinstance(service_scans_config['username_wordlist'], str):
|
||||||
|
username_wordlist = service_scans_config['username_wordlist']
|
||||||
|
|
||||||
|
if 'password_wordlist' in service_scans_config:
|
||||||
|
if isinstance(service_scans_config['password_wordlist'], str):
|
||||||
|
password_wordlist = service_scans_config['password_wordlist']
|
||||||
|
|
||||||
|
async def read_stream(stream, target, tag='?', patterns=[], color=Fore.BLUE):
|
||||||
|
address = target.address
|
||||||
|
while True:
|
||||||
|
line = await stream.readline()
|
||||||
|
if line:
|
||||||
|
line = str(line.rstrip(), 'utf8', 'ignore')
|
||||||
|
debug(color + '[' + Style.BRIGHT + address + ' ' + tag + Style.NORMAL + '] ' + Fore.RESET + '{line}', color=color)
|
||||||
|
|
||||||
|
for p in global_patterns:
|
||||||
|
matches = re.findall(p['pattern'], line)
|
||||||
|
if 'description' in p:
|
||||||
|
for match in matches:
|
||||||
|
if verbose >= 1:
|
||||||
|
info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}' + p['description'].replace('{match}', '{bblue}{match}{crst}{bmagenta}') + '{rst}')
|
||||||
|
async with target.lock:
|
||||||
|
with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file:
|
||||||
|
file.writelines(e('{tag} - ' + p['description'] + '\n\n'))
|
||||||
|
else:
|
||||||
|
for match in matches:
|
||||||
|
if verbose >= 1:
|
||||||
|
info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}Matched Pattern: {bblue}{match}{rst}')
|
||||||
|
async with target.lock:
|
||||||
|
with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file:
|
||||||
|
file.writelines(e('{tag} - Matched Pattern: {match}\n\n'))
|
||||||
|
|
||||||
|
for p in patterns:
|
||||||
|
matches = re.findall(p['pattern'], line)
|
||||||
|
if 'description' in p:
|
||||||
|
for match in matches:
|
||||||
|
if verbose >= 1:
|
||||||
|
info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}' + p['description'].replace('{match}', '{bblue}{match}{crst}{bmagenta}') + '{rst}')
|
||||||
|
async with target.lock:
|
||||||
|
with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file:
|
||||||
|
file.writelines(e('{tag} - ' + p['description'] + '\n\n'))
|
||||||
|
else:
|
||||||
|
for match in matches:
|
||||||
|
if verbose >= 1:
|
||||||
|
info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}Matched Pattern: {bblue}{match}{rst}')
|
||||||
|
async with target.lock:
|
||||||
|
with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file:
|
||||||
|
file.writelines(e('{tag} - Matched Pattern: {match}\n\n'))
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
async def run_cmd(semaphore, cmd, target, tag='?', patterns=[]):
|
||||||
|
async with semaphore:
|
||||||
|
address = target.address
|
||||||
|
scandir = target.scandir
|
||||||
|
|
||||||
|
info('Running task {bgreen}{tag}{rst} on {byellow}{address}{rst}' + (' with {bblue}{cmd}{rst}' if verbose >= 1 else ''))
|
||||||
|
|
||||||
|
async with target.lock:
|
||||||
|
with open(os.path.join(scandir, '_commands.log'), 'a') as file:
|
||||||
|
file.writelines(e('{cmd}\n\n'))
|
||||||
|
|
||||||
|
start_time = time.time()
|
||||||
|
process = await asyncio.create_subprocess_shell(cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, executable='/bin/bash')
|
||||||
|
async with target.lock:
|
||||||
|
target.running_tasks.append(tag)
|
||||||
|
|
||||||
|
await asyncio.wait([
|
||||||
|
read_stream(process.stdout, target, tag=tag, patterns=patterns),
|
||||||
|
read_stream(process.stderr, target, tag=tag, patterns=patterns, color=Fore.RED)
|
||||||
|
])
|
||||||
|
|
||||||
|
await process.wait()
|
||||||
|
async with target.lock:
|
||||||
|
target.running_tasks.remove(tag)
|
||||||
|
elapsed_time = calculate_elapsed_time(start_time)
|
||||||
|
|
||||||
|
if process.returncode != 0:
|
||||||
|
error('Task {bred}{tag}{rst} on {byellow}{address}{rst} returned non-zero exit code: {process.returncode}')
|
||||||
|
async with target.lock:
|
||||||
|
with open(os.path.join(scandir, '_errors.log'), 'a') as file:
|
||||||
|
file.writelines(e('[*] Task {tag} returned non-zero exit code: {process.returncode}. Command: {cmd}\n'))
|
||||||
|
else:
|
||||||
|
info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} finished successfully in {elapsed_time}')
|
||||||
|
|
||||||
|
return {'returncode': process.returncode, 'name': 'run_cmd'}
|
||||||
|
|
||||||
|
async def parse_port_scan(stream, tag, target, pattern):
|
||||||
|
address = target.address
|
||||||
|
ports = []
|
||||||
|
|
||||||
|
while True:
|
||||||
|
line = await stream.readline()
|
||||||
|
if line:
|
||||||
|
line = str(line.rstrip(), 'utf8', 'ignore')
|
||||||
|
debug(Fore.BLUE + '[' + Style.BRIGHT + address + ' ' + tag + Style.NORMAL + '] ' + Fore.RESET + '{line}', color=Fore.BLUE)
|
||||||
|
|
||||||
|
parse_match = re.search(pattern, line)
|
||||||
|
if parse_match:
|
||||||
|
ports.append(parse_match.group('port'))
|
||||||
|
|
||||||
|
|
||||||
|
for p in global_patterns:
|
||||||
|
matches = re.findall(p['pattern'], line)
|
||||||
|
if 'description' in p:
|
||||||
|
for match in matches:
|
||||||
|
if verbose >= 1:
|
||||||
|
info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}' + p['description'].replace('{match}', '{bblue}{match}{crst}{bmagenta}') + '{rst}')
|
||||||
|
async with target.lock:
|
||||||
|
with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file:
|
||||||
|
file.writelines(e('{tag} - ' + p['description'] + '\n\n'))
|
||||||
|
else:
|
||||||
|
for match in matches:
|
||||||
|
if verbose >= 1:
|
||||||
|
info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}Matched Pattern: {bblue}{match}{rst}')
|
||||||
|
async with target.lock:
|
||||||
|
with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file:
|
||||||
|
file.writelines(e('{tag} - Matched Pattern: {match}\n\n'))
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
return ports
|
||||||
|
|
||||||
|
async def parse_service_detection(stream, tag, target, pattern):
|
||||||
|
address = target.address
|
||||||
|
services = []
|
||||||
|
|
||||||
|
while True:
|
||||||
|
line = await stream.readline()
|
||||||
|
if line:
|
||||||
|
line = str(line.rstrip(), 'utf8', 'ignore')
|
||||||
|
debug(Fore.BLUE + '[' + Style.BRIGHT + address + ' ' + tag + Style.NORMAL + '] ' + Fore.RESET + '{line}', color=Fore.BLUE)
|
||||||
|
|
||||||
|
parse_match = re.search(pattern, line)
|
||||||
|
if parse_match:
|
||||||
|
services.append((parse_match.group('protocol').lower(), int(parse_match.group('port')), parse_match.group('service')))
|
||||||
|
|
||||||
|
for p in global_patterns:
|
||||||
|
matches = re.findall(p['pattern'], line)
|
||||||
|
if 'description' in p:
|
||||||
|
for match in matches:
|
||||||
|
if verbose >= 1:
|
||||||
|
info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}' + p['description'].replace('{match}', '{bblue}{match}{crst}{bmagenta}') + '{rst}')
|
||||||
|
async with target.lock:
|
||||||
|
with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file:
|
||||||
|
file.writelines(e('{tag} - ' + p['description'] + '\n\n'))
|
||||||
|
else:
|
||||||
|
for match in matches:
|
||||||
|
if verbose >= 1:
|
||||||
|
info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}Matched Pattern: {bblue}{match}{rst}')
|
||||||
|
async with target.lock:
|
||||||
|
with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file:
|
||||||
|
file.writelines(e('{tag} - Matched Pattern: {match}\n\n'))
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
return services
|
||||||
|
|
||||||
|
async def run_portscan(semaphore, tag, target, service_detection, port_scan=None):
|
||||||
|
async with semaphore:
|
||||||
|
|
||||||
|
address = target.address
|
||||||
|
scandir = target.scandir
|
||||||
|
nmap_extra = nmap
|
||||||
|
|
||||||
|
ports = ''
|
||||||
|
if port_scan is not None:
|
||||||
|
command = e(port_scan[0])
|
||||||
|
pattern = port_scan[1]
|
||||||
|
|
||||||
|
info('Running port scan {bgreen}{tag}{rst} on {byellow}{address}{rst}' + (' with {bblue}{command}{rst}' if verbose >= 1 else ''))
|
||||||
|
|
||||||
|
async with target.lock:
|
||||||
|
with open(os.path.join(scandir, '_commands.log'), 'a') as file:
|
||||||
|
file.writelines(e('{command}\n\n'))
|
||||||
|
|
||||||
|
start_time = time.time()
|
||||||
|
process = await asyncio.create_subprocess_shell(command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, executable='/bin/bash')
|
||||||
|
async with target.lock:
|
||||||
|
target.running_tasks.append(tag)
|
||||||
|
|
||||||
|
output = [
|
||||||
|
parse_port_scan(process.stdout, tag, target, pattern),
|
||||||
|
read_stream(process.stderr, target, tag=tag, color=Fore.RED)
|
||||||
|
]
|
||||||
|
|
||||||
|
results = await asyncio.gather(*output)
|
||||||
|
|
||||||
|
await process.wait()
|
||||||
|
async with target.lock:
|
||||||
|
target.running_tasks.remove(tag)
|
||||||
|
elapsed_time = calculate_elapsed_time(start_time)
|
||||||
|
|
||||||
|
if process.returncode != 0:
|
||||||
|
error('Port scan {bred}{tag}{rst} on {byellow}{address}{rst} returned non-zero exit code: {process.returncode}')
|
||||||
|
async with target.lock:
|
||||||
|
with open(os.path.join(scandir, '_errors.log'), 'a') as file:
|
||||||
|
file.writelines(e('[*] Port scan {tag} returned non-zero exit code: {process.returncode}. Command: {command}\n'))
|
||||||
|
return {'returncode': process.returncode}
|
||||||
|
else:
|
||||||
|
info('Port scan {bgreen}{tag}{rst} on {byellow}{address}{rst} finished successfully in {elapsed_time}')
|
||||||
|
|
||||||
|
ports = results[0]
|
||||||
|
if len(ports) == 0:
|
||||||
|
return {'returncode': -1}
|
||||||
|
|
||||||
|
ports = ','.join(ports)
|
||||||
|
|
||||||
|
command = e(service_detection[0])
|
||||||
|
pattern = service_detection[1]
|
||||||
|
|
||||||
|
info('Running service detection {bgreen}{tag}{rst} on {byellow}{address}{rst}' + (' with {bblue}{command}{rst}' if verbose >= 1 else ''))
|
||||||
|
|
||||||
|
async with target.lock:
|
||||||
|
with open(os.path.join(scandir, '_commands.log'), 'a') as file:
|
||||||
|
file.writelines(e('{command}\n\n'))
|
||||||
|
|
||||||
|
start_time = time.time()
|
||||||
|
process = await asyncio.create_subprocess_shell(command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, executable='/bin/bash')
|
||||||
|
async with target.lock:
|
||||||
|
target.running_tasks.append(tag)
|
||||||
|
|
||||||
|
output = [
|
||||||
|
parse_service_detection(process.stdout, tag, target, pattern),
|
||||||
|
read_stream(process.stderr, target, tag=tag, color=Fore.RED)
|
||||||
|
]
|
||||||
|
|
||||||
|
results = await asyncio.gather(*output)
|
||||||
|
|
||||||
|
await process.wait()
|
||||||
|
async with target.lock:
|
||||||
|
target.running_tasks.remove(tag)
|
||||||
|
elapsed_time = calculate_elapsed_time(start_time)
|
||||||
|
|
||||||
|
if process.returncode != 0:
|
||||||
|
error('Service detection {bred}{tag}{rst} on {byellow}{address}{rst} returned non-zero exit code: {process.returncode}')
|
||||||
|
async with target.lock:
|
||||||
|
with open(os.path.join(scandir, '_errors.log'), 'a') as file:
|
||||||
|
file.writelines(e('[*] Service detection {tag} returned non-zero exit code: {process.returncode}. Command: {command}\n'))
|
||||||
|
else:
|
||||||
|
info('Service detection {bgreen}{tag}{rst} on {byellow}{address}{rst} finished successfully in {elapsed_time}')
|
||||||
|
|
||||||
|
services = results[0]
|
||||||
|
|
||||||
|
return {'returncode': process.returncode, 'name': 'run_portscan', 'services': services}
|
||||||
|
|
||||||
|
async def start_heartbeat(target, period=60):
|
||||||
|
while True:
|
||||||
|
await asyncio.sleep(period)
|
||||||
|
async with target.lock:
|
||||||
|
tasks = target.running_tasks
|
||||||
|
count = len(tasks)
|
||||||
|
|
||||||
|
tasks_list = ''
|
||||||
|
if verbose >= 1:
|
||||||
|
tasks_list = ': {bgreen}' + ', '.join(tasks) + '{rst}'
|
||||||
|
|
||||||
|
current_time = datetime.now().strftime('%H:%M:%S')
|
||||||
|
|
||||||
|
if count > 1:
|
||||||
|
info('{bgreen}[{current_time}]{rst} - There are {byellow}{count}{rst} tasks still running on {byellow}{target.address}{rst}' + tasks_list)
|
||||||
|
elif count == 1:
|
||||||
|
info('{bgreen}[{current_time}]{rst} - There is {byellow}1{rst} task still running on {byellow}{target.address}{rst}' + tasks_list)
|
||||||
|
|
||||||
|
async def scan_services(loop, semaphore, target):
|
||||||
|
address = target.address
|
||||||
|
scandir = target.scandir
|
||||||
|
pending = []
|
||||||
|
|
||||||
|
heartbeat = loop.create_task(start_heartbeat(target, period=heartbeat_interval))
|
||||||
|
|
||||||
|
for profile in port_scan_profiles_config:
|
||||||
|
if profile == port_scan_profile:
|
||||||
|
for scan in port_scan_profiles_config[profile]:
|
||||||
|
service_detection = (port_scan_profiles_config[profile][scan]['service-detection']['command'], port_scan_profiles_config[profile][scan]['service-detection']['pattern'])
|
||||||
|
if 'port-scan' in port_scan_profiles_config[profile][scan]:
|
||||||
|
port_scan = (port_scan_profiles_config[profile][scan]['port-scan']['command'], port_scan_profiles_config[profile][scan]['port-scan']['pattern'])
|
||||||
|
pending.append(run_portscan(semaphore, scan, target, service_detection, port_scan))
|
||||||
|
else:
|
||||||
|
pending.append(run_portscan(semaphore, scan, target, service_detection))
|
||||||
|
break
|
||||||
|
|
||||||
|
services = []
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if not pending:
|
||||||
|
heartbeat.cancel()
|
||||||
|
break
|
||||||
|
|
||||||
|
done, pending = await asyncio.wait(pending, return_when=FIRST_COMPLETED)
|
||||||
|
|
||||||
|
for task in done:
|
||||||
|
result = task.result()
|
||||||
|
|
||||||
|
if result['returncode'] == 0:
|
||||||
|
if result['name'] == 'run_portscan':
|
||||||
|
for service_tuple in result['services']:
|
||||||
|
if service_tuple not in services:
|
||||||
|
services.append(service_tuple)
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
protocol = service_tuple[0]
|
||||||
|
port = service_tuple[1]
|
||||||
|
service = service_tuple[2]
|
||||||
|
|
||||||
|
info('Found {bmagenta}{service}{rst} on {bmagenta}{protocol}/{port}{rst} on target {byellow}{address}{rst}')
|
||||||
|
|
||||||
|
if not only_scans_dir:
|
||||||
|
with open(os.path.join(target.reportdir, 'notes.txt'), 'a') as file:
|
||||||
|
file.writelines(e('[*] {service} found on {protocol}/{port}.\n\n\n\n'))
|
||||||
|
|
||||||
|
if protocol == 'udp':
|
||||||
|
nmap_extra = nmap + " -sU"
|
||||||
|
else:
|
||||||
|
nmap_extra = nmap
|
||||||
|
|
||||||
|
secure = True if 'ssl' in service or 'tls' in service else False
|
||||||
|
|
||||||
|
# Special cases for HTTP.
|
||||||
|
scheme = 'https' if 'https' in service or 'ssl' in service or 'tls' in service else 'http'
|
||||||
|
|
||||||
|
if service.startswith('ssl/') or service.startswith('tls/'):
|
||||||
|
service = service[4:]
|
||||||
|
|
||||||
|
for service_scan in service_scans_config:
|
||||||
|
# Skip over configurable variables since the python toml parser cannot iterate over tables only.
|
||||||
|
if service_scan in ['username_wordlist', 'password_wordlist']:
|
||||||
|
continue
|
||||||
|
|
||||||
|
ignore_service = False
|
||||||
|
if 'ignore-service-names' in service_scans_config[service_scan]:
|
||||||
|
for ignore_service_name in service_scans_config[service_scan]['ignore-service-names']:
|
||||||
|
if re.search(ignore_service_name, service):
|
||||||
|
ignore_service = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if ignore_service:
|
||||||
|
continue
|
||||||
|
|
||||||
|
matched_service = False
|
||||||
|
|
||||||
|
if 'service-names' in service_scans_config[service_scan]:
|
||||||
|
for service_name in service_scans_config[service_scan]['service-names']:
|
||||||
|
if re.search(service_name, service):
|
||||||
|
matched_service = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not matched_service:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if 'manual' in service_scans_config[service_scan]:
|
||||||
|
heading = False
|
||||||
|
with open(os.path.join(scandir, '_manual_commands.txt'), 'a') as file:
|
||||||
|
for manual in service_scans_config[service_scan]['manual']:
|
||||||
|
if 'description' in manual:
|
||||||
|
if not heading:
|
||||||
|
file.writelines(e('[*] {service} on {protocol}/{port}\n\n'))
|
||||||
|
heading = True
|
||||||
|
description = manual['description']
|
||||||
|
file.writelines(e('\t[-] {description}\n\n'))
|
||||||
|
if 'commands' in manual:
|
||||||
|
if not heading:
|
||||||
|
file.writelines(e('[*] {service} on {protocol}/{port}\n\n'))
|
||||||
|
heading = True
|
||||||
|
for manual_command in manual['commands']:
|
||||||
|
manual_command = e(manual_command)
|
||||||
|
file.writelines('\t\t' + e('{manual_command}\n\n'))
|
||||||
|
if heading:
|
||||||
|
file.writelines('\n')
|
||||||
|
|
||||||
|
if 'scan' in service_scans_config[service_scan]:
|
||||||
|
for scan in service_scans_config[service_scan]['scan']:
|
||||||
|
|
||||||
|
if 'name' in scan:
|
||||||
|
name = scan['name']
|
||||||
|
if 'command' in scan:
|
||||||
|
tag = e('{protocol}/{port}/{name}')
|
||||||
|
command = scan['command']
|
||||||
|
|
||||||
|
if 'ports' in scan:
|
||||||
|
port_match = False
|
||||||
|
|
||||||
|
if protocol == 'tcp':
|
||||||
|
if 'tcp' in scan['ports']:
|
||||||
|
for tcp_port in scan['ports']['tcp']:
|
||||||
|
if port == tcp_port:
|
||||||
|
port_match = True
|
||||||
|
break
|
||||||
|
elif protocol == 'udp':
|
||||||
|
if 'udp' in scan['ports']:
|
||||||
|
for udp_port in scan['ports']['udp']:
|
||||||
|
if port == udp_port:
|
||||||
|
port_match = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if port_match == False:
|
||||||
|
warn(Fore.YELLOW + '[' + Style.BRIGHT + tag + Style.NORMAL + '] Scan cannot be run against {protocol} port {port}. Skipping.' + Fore.RESET)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if 'run_once' in scan and scan['run_once'] == True:
|
||||||
|
scan_tuple = (name,)
|
||||||
|
if scan_tuple in target.scans:
|
||||||
|
warn(Fore.YELLOW + '[' + Style.BRIGHT + tag + ' on ' + address + Style.NORMAL + '] Scan should only be run once and it appears to have already been queued. Skipping.' + Fore.RESET)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
target.scans.append(scan_tuple)
|
||||||
|
else:
|
||||||
|
scan_tuple = (protocol, port, service, name)
|
||||||
|
if scan_tuple in target.scans:
|
||||||
|
warn(Fore.YELLOW + '[' + Style.BRIGHT + tag + ' on ' + address + Style.NORMAL + '] Scan appears to have already been queued, but it is not marked as run_once in service-scans.toml. Possible duplicate tag? Skipping.' + Fore.RESET)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
target.scans.append(scan_tuple)
|
||||||
|
|
||||||
|
patterns = []
|
||||||
|
if 'pattern' in scan:
|
||||||
|
patterns = scan['pattern']
|
||||||
|
|
||||||
|
pending.add(asyncio.ensure_future(run_cmd(semaphore, e(command), target, tag=tag, patterns=patterns)))
|
||||||
|
|
||||||
|
def scan_host(target, concurrent_scans):
|
||||||
|
start_time = time.time()
|
||||||
|
info('Scanning target {byellow}{target.address}{rst}')
|
||||||
|
|
||||||
|
if single_target:
|
||||||
|
basedir = os.path.abspath(outdir)
|
||||||
|
else:
|
||||||
|
basedir = os.path.abspath(os.path.join(outdir, target.address + srvname))
|
||||||
|
target.basedir = basedir
|
||||||
|
os.makedirs(basedir, exist_ok=True)
|
||||||
|
|
||||||
|
if not only_scans_dir:
|
||||||
|
exploitdir = os.path.abspath(os.path.join(basedir, 'exploit'))
|
||||||
|
os.makedirs(exploitdir, exist_ok=True)
|
||||||
|
|
||||||
|
lootdir = os.path.abspath(os.path.join(basedir, 'loot'))
|
||||||
|
os.makedirs(lootdir, exist_ok=True)
|
||||||
|
|
||||||
|
reportdir = os.path.abspath(os.path.join(basedir, 'report'))
|
||||||
|
target.reportdir = reportdir
|
||||||
|
os.makedirs(reportdir, exist_ok=True)
|
||||||
|
|
||||||
|
open(os.path.abspath(os.path.join(reportdir, 'local.txt')), 'a').close()
|
||||||
|
open(os.path.abspath(os.path.join(reportdir, 'proof.txt')), 'a').close()
|
||||||
|
|
||||||
|
screenshotdir = os.path.abspath(os.path.join(reportdir, 'screenshots'))
|
||||||
|
os.makedirs(screenshotdir, exist_ok=True)
|
||||||
|
|
||||||
|
scandir = os.path.abspath(os.path.join(basedir, 'scans'))
|
||||||
|
target.scandir = scandir
|
||||||
|
os.makedirs(scandir, exist_ok=True)
|
||||||
|
|
||||||
|
os.makedirs(os.path.abspath(os.path.join(scandir, 'xml')), exist_ok=True)
|
||||||
|
|
||||||
|
# Use a lock when writing to specific files that may be written to by other asynchronous functions.
|
||||||
|
target.lock = asyncio.Lock()
|
||||||
|
|
||||||
|
# Get event loop for current process.
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
|
||||||
|
# Create a semaphore to limit number of concurrent scans.
|
||||||
|
semaphore = asyncio.Semaphore(concurrent_scans)
|
||||||
|
|
||||||
|
try:
|
||||||
|
loop.run_until_complete(scan_services(loop, semaphore, target))
|
||||||
|
elapsed_time = calculate_elapsed_time(start_time)
|
||||||
|
info('Finished scanning target {byellow}{target.address}{rst} in {elapsed_time}')
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
class Target:
|
||||||
|
def __init__(self, address):
|
||||||
|
self.address = address
|
||||||
|
self.basedir = ''
|
||||||
|
self.reportdir = ''
|
||||||
|
self.scandir = ''
|
||||||
|
self.scans = []
|
||||||
|
self.lock = None
|
||||||
|
self.running_tasks = []
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='Network reconnaissance tool to port scan and automatically enumerate services found on multiple targets.')
|
||||||
|
parser.add_argument('targets', action='store', help='IP addresses (e.g. 10.0.0.1), CIDR notation (e.g. 10.0.0.1/24), or resolvable hostnames (e.g. foo.bar) to scan.', nargs="*")
|
||||||
|
parser.add_argument('-t', '--targets', action='store', type=str, default='', dest='target_file', help='Read targets from file.')
|
||||||
|
parser.add_argument('-ct', '--concurrent-targets', action='store', metavar='<number>', type=int, default=5, help='The maximum number of target hosts to scan concurrently. Default: %(default)s')
|
||||||
|
parser.add_argument('-cs', '--concurrent-scans', action='store', metavar='<number>', type=int, default=10, help='The maximum number of scans to perform per target host. Default: %(default)s')
|
||||||
|
parser.add_argument('--profile', action='store', default='default', dest='profile_name', help='The port scanning profile to use (defined in port-scan-profiles.toml). Default: %(default)s')
|
||||||
|
parser.add_argument('-o', '--output', action='store', default='results', dest='output_dir', help='The output directory for results. Default: %(default)s')
|
||||||
|
parser.add_argument('--single-target', action='store_true', default=False, help='Only scan a single target. A directory named after the target will not be created. Instead, the directory structure will be created within the output directory. Default: false')
|
||||||
|
parser.add_argument('--only-scans-dir', action='store_true', default=False, help='Only create the "scans" directory for results. Other directories (e.g. exploit, loot, report) will not be created. Default: false')
|
||||||
|
parser.add_argument('--heartbeat', action='store', type=int, default=60, help='Specifies the heartbeat interval (in seconds) for task status messages. Default: %(default)s')
|
||||||
|
nmap_group = parser.add_mutually_exclusive_group()
|
||||||
|
nmap_group.add_argument('--nmap', action='store', default='-vv --reason -Pn', help='Override the {nmap_extra} variable in scans. Default: %(default)s')
|
||||||
|
nmap_group.add_argument('--nmap-append', action='store', default='', help='Append to the default {nmap_extra} variable in scans.')
|
||||||
|
parser.add_argument('-v', '--verbose', action='count', default=0, help='Enable verbose output. Repeat for more verbosity.')
|
||||||
|
parser.add_argument('--disable-sanity-checks', action='store_true', default=False, help='Disable sanity checks that would otherwise prevent the scans from running. Default: false')
|
||||||
|
parser.error = lambda s: fail(s[0].upper() + s[1:])
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
single_target = args.single_target
|
||||||
|
only_scans_dir = args.only_scans_dir
|
||||||
|
|
||||||
|
errors = False
|
||||||
|
|
||||||
|
if args.concurrent_targets <= 0:
|
||||||
|
error('Argument -ch/--concurrent-targets: must be at least 1.')
|
||||||
|
errors = True
|
||||||
|
|
||||||
|
concurrent_scans = args.concurrent_scans
|
||||||
|
|
||||||
|
if concurrent_scans <= 0:
|
||||||
|
error('Argument -ct/--concurrent-scans: must be at least 1.')
|
||||||
|
errors = True
|
||||||
|
|
||||||
|
port_scan_profile = args.profile_name
|
||||||
|
|
||||||
|
found_scan_profile = False
|
||||||
|
for profile in port_scan_profiles_config:
|
||||||
|
if profile == port_scan_profile:
|
||||||
|
found_scan_profile = True
|
||||||
|
for scan in port_scan_profiles_config[profile]:
|
||||||
|
if 'service-detection' not in port_scan_profiles_config[profile][scan]:
|
||||||
|
error('The {profile}.{scan} scan does not have a defined service-detection section. Every scan must at least have a service-detection section defined with a command and a corresponding pattern that extracts the protocol (TCP/UDP), port, and service from the result.')
|
||||||
|
errors = True
|
||||||
|
else:
|
||||||
|
if 'command' not in port_scan_profiles_config[profile][scan]['service-detection']:
|
||||||
|
error('The {profile}.{scan}.service-detection section does not have a command defined. Every service-detection section must have a command and a corresponding pattern that extracts the protocol (TCP/UDP), port, and service from the results.')
|
||||||
|
errors = True
|
||||||
|
else:
|
||||||
|
if '{ports}' in port_scan_profiles_config[profile][scan]['service-detection']['command'] and 'port-scan' not in port_scan_profiles_config[profile][scan]:
|
||||||
|
error('The {profile}.{scan}.service-detection command appears to reference a port list but there is no port-scan section defined in {profile}.{scan}. Define a port-scan section with a command and corresponding pattern that extracts port numbers from the result, or replace the reference with a static list of ports.')
|
||||||
|
errors = True
|
||||||
|
|
||||||
|
if 'pattern' not in port_scan_profiles_config[profile][scan]['service-detection']:
|
||||||
|
error('The {profile}.{scan}.service-detection section does not have a pattern defined. Every service-detection section must have a command and a corresponding pattern that extracts the protocol (TCP/UDP), port, and service from the results.')
|
||||||
|
errors = True
|
||||||
|
else:
|
||||||
|
if not all(x in port_scan_profiles_config[profile][scan]['service-detection']['pattern'] for x in ['(?P<port>', '(?P<protocol>', '(?P<service>']):
|
||||||
|
error('The {profile}.{scan}.service-detection pattern does not contain one or more of the following matching groups: port, protocol, service. Ensure that all three of these matching groups are defined and capture the relevant data, e.g. (?P<port>\d+)')
|
||||||
|
errors = True
|
||||||
|
|
||||||
|
if 'port-scan' in port_scan_profiles_config[profile][scan]:
|
||||||
|
if 'command' not in port_scan_profiles_config[profile][scan]['port-scan']:
|
||||||
|
error('The {profile}.{scan}.port-scan section does not have a command defined. Every port-scan section must have a command and a corresponding pattern that extracts the port from the results.')
|
||||||
|
errors = True
|
||||||
|
|
||||||
|
if 'pattern' not in port_scan_profiles_config[profile][scan]['port-scan']:
|
||||||
|
error('The {profile}.{scan}.port-scan section does not have a pattern defined. Every port-scan section must have a command and a corresponding pattern that extracts the port from the results.')
|
||||||
|
errors = True
|
||||||
|
else:
|
||||||
|
if '(?P<port>' not in port_scan_profiles_config[profile][scan]['port-scan']['pattern']:
|
||||||
|
error('The {profile}.{scan}.port-scan pattern does not contain a port matching group. Ensure that the port matching group is defined and captures the relevant data, e.g. (?P<port>\d+)')
|
||||||
|
errors = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not found_scan_profile:
|
||||||
|
error('Argument --profile: must reference a port scan profile defined in {port_scan_profiles_config_file}. No such profile found: {port_scan_profile}')
|
||||||
|
errors = True
|
||||||
|
|
||||||
|
heartbeat_interval = args.heartbeat
|
||||||
|
|
||||||
|
nmap = args.nmap
|
||||||
|
if args.nmap_append:
|
||||||
|
nmap += " " + args.nmap_append
|
||||||
|
|
||||||
|
outdir = args.output_dir
|
||||||
|
srvname = ''
|
||||||
|
verbose = args.verbose
|
||||||
|
|
||||||
|
raw_targets = args.targets
|
||||||
|
targets = []
|
||||||
|
|
||||||
|
if len(args.target_file) > 0:
|
||||||
|
if not os.path.isfile(args.target_file):
|
||||||
|
error('The target file {args.target_file} was not found.')
|
||||||
|
sys.exit(1)
|
||||||
|
try:
|
||||||
|
with open(args.target_file, 'r') as f:
|
||||||
|
lines = f.read()
|
||||||
|
for line in lines.splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith('#') or len(line) == 0: continue
|
||||||
|
if line not in raw_targets:
|
||||||
|
raw_targets.append(line)
|
||||||
|
except OSError:
|
||||||
|
error('The target file {args.target_file} could not be read.')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
for target in raw_targets:
|
||||||
|
try:
|
||||||
|
ip = str(ipaddress.ip_address(target))
|
||||||
|
|
||||||
|
if ip not in targets:
|
||||||
|
targets.append(ip)
|
||||||
|
except ValueError:
|
||||||
|
|
||||||
|
try:
|
||||||
|
target_range = ipaddress.ip_network(target, strict=False)
|
||||||
|
if not args.disable_sanity_checks and target_range.num_addresses > 256:
|
||||||
|
error(target + ' contains ' + str(target_range.num_addresses) + ' addresses. Check that your CIDR notation is correct. If it is, re-run with the --disable-sanity-checks option to suppress this check.')
|
||||||
|
errors = True
|
||||||
|
else:
|
||||||
|
for ip in target_range.hosts():
|
||||||
|
ip = str(ip)
|
||||||
|
if ip not in targets:
|
||||||
|
targets.append(ip)
|
||||||
|
except ValueError:
|
||||||
|
|
||||||
|
try:
|
||||||
|
ip = socket.gethostbyname(target)
|
||||||
|
|
||||||
|
if target not in targets:
|
||||||
|
targets.append(target)
|
||||||
|
except socket.gaierror:
|
||||||
|
error(target + ' does not appear to be a valid IP address, IP range, or resolvable hostname.')
|
||||||
|
errors = True
|
||||||
|
|
||||||
|
if len(targets) == 0:
|
||||||
|
error('You must specify at least one target to scan!')
|
||||||
|
errors = True
|
||||||
|
|
||||||
|
if single_target and len(targets) != 1:
|
||||||
|
error('You cannot provide more than one target when scanning in single-target mode.')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if not args.disable_sanity_checks and len(targets) > 256:
|
||||||
|
error('A total of ' + str(len(targets)) + ' targets would be scanned. If this is correct, re-run with the --disable-sanity-checks option to suppress this check.')
|
||||||
|
errors = True
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
with ProcessPoolExecutor(max_workers=args.concurrent_targets) as executor:
|
||||||
|
start_time = time.time()
|
||||||
|
futures = []
|
||||||
|
|
||||||
|
for address in targets:
|
||||||
|
target = Target(address)
|
||||||
|
futures.append(executor.submit(scan_host, target, concurrent_scans))
|
||||||
|
|
||||||
|
try:
|
||||||
|
for future in as_completed(futures):
|
||||||
|
future.result()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
for future in futures:
|
||||||
|
future.cancel()
|
||||||
|
executor.shutdown(wait=False)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
elapsed_time = calculate_elapsed_time(start_time)
|
||||||
|
info('{bgreen}Finished scanning all targets in {elapsed_time}!{rst}')
|
8
config/global-patterns.toml
Normal file
8
config/global-patterns.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Patterns defined in this file will be checked against every line of output (e.g. port scans and service scans)
|
||||||
|
|
||||||
|
[[pattern]]
|
||||||
|
description = 'Nmap script found a potential vulnerability. ({match})'
|
||||||
|
pattern = 'State: (?:(?:LIKELY\_?)?VULNERABLE)'
|
||||||
|
|
||||||
|
[[pattern]]
|
||||||
|
pattern = '(?i)unauthorized'
|
45
config/port-scan-profiles.toml
Normal file
45
config/port-scan-profiles.toml
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
[default]
|
||||||
|
|
||||||
|
[default.nmap-quick]
|
||||||
|
|
||||||
|
[default.nmap-quick.service-detection]
|
||||||
|
command = 'nmap {nmap_extra} -sV -sC --version-all -oN "{scandir}/_quick_tcp_nmap.txt" -oX "{scandir}/xml/_quick_tcp_nmap.xml" {address}'
|
||||||
|
pattern = '^(?P<port>\d+)\/(?P<protocol>(tcp|udp))(.*)open(\s*)(?P<service>[\w\-\/]+)(\s*)(.*)$'
|
||||||
|
|
||||||
|
[default.nmap-full-tcp]
|
||||||
|
|
||||||
|
[default.nmap-full-tcp.service-detection]
|
||||||
|
command = 'nmap {nmap_extra} -A --osscan-guess --version-all -p- -oN "{scandir}/_full_tcp_nmap.txt" -oX "{scandir}/xml/_full_tcp_nmap.xml" {address}'
|
||||||
|
pattern = '^(?P<port>\d+)\/(?P<protocol>(tcp|udp))(.*)open(\s*)(?P<service>[\w\-\/]+)(\s*)(.*)$'
|
||||||
|
|
||||||
|
[default.nmap-top-20-udp]
|
||||||
|
|
||||||
|
[default.nmap-top-20-udp.service-detection]
|
||||||
|
command = 'nmap {nmap_extra} -sU -A --top-ports=20 --version-all -oN "{scandir}/_top_20_udp_nmap.txt" -oX "{scandir}/xml/_top_20_udp_nmap.xml" {address}'
|
||||||
|
pattern = '^(?P<port>\d+)\/(?P<protocol>(tcp|udp))(.*)open(\s*)(?P<service>[\w\-\/]+)(\s*)(.*)$'
|
||||||
|
|
||||||
|
[quick]
|
||||||
|
|
||||||
|
[quick.nmap-quick]
|
||||||
|
|
||||||
|
[quick.nmap-quick.service-detection]
|
||||||
|
command = 'nmap {nmap_extra} -sV --version-all -oN "{scandir}/_quick_tcp_nmap.txt" -oX "{scandir}/xml/_quick_tcp_nmap.xml" {address}'
|
||||||
|
pattern = '^(?P<port>\d+)\/(?P<protocol>(tcp|udp))(.*)open(\s*)(?P<service>[\w\-\/]+)(\s*)(.*)$'
|
||||||
|
|
||||||
|
[quick.nmap-top-20-udp]
|
||||||
|
|
||||||
|
[quick.nmap-top-20-udp.service-detection]
|
||||||
|
command = 'nmap {nmap_extra} -sU -A --top-ports=20 --version-all -oN "{scandir}/_top_20_udp_nmap.txt" -oX "{scandir}/xml/_top_20_udp_nmap.xml" {address}'
|
||||||
|
pattern = '^(?P<port>\d+)\/(?P<protocol>(tcp|udp))(.*)open(\s*)(?P<service>[\w\-\/]+)(\s*)(.*)$'
|
||||||
|
|
||||||
|
[udp]
|
||||||
|
|
||||||
|
[udp.udp-top-20]
|
||||||
|
|
||||||
|
[udp.udp-top-20.port-scan]
|
||||||
|
command = 'unicornscan -mU -p 631,161,137,123,138,1434,445,135,67,53,139,500,68,520,1900,4500,514,49152,162,69 {address} 2>&1 | tee "{scandir}/_top_20_udp_unicornscan.txt"'
|
||||||
|
pattern = '^UDP open\s*[\w-]+\[\s*(?P<port>\d+)\].*$'
|
||||||
|
|
||||||
|
[udp.udp-top-20.service-detection]
|
||||||
|
command = 'nmap {nmap_extra} -sU -A -p {ports} --version-all -oN "{scandir}/_top_20_udp_nmap.txt" -oX "{scandir}/xml/_top_20_udp_nmap.xml" {address}'
|
||||||
|
pattern = '^(?P<port>\d+)\/(?P<protocol>(udp))(.*)open(\s*)(?P<service>[\w\-\/]+)(\s*)(.*)$'
|
577
config/service-scans.toml
Normal file
577
config/service-scans.toml
Normal file
@ -0,0 +1,577 @@
|
|||||||
|
# Configurable Variables
|
||||||
|
username_wordlist = '/usr/share/seclists/Usernames/top-usernames-shortlist.txt'
|
||||||
|
password_wordlist = '/usr/share/seclists/Passwords/darkweb2017-top100.txt'
|
||||||
|
|
||||||
|
[all-services] # Define scans here that you want to run against all services.
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'.+'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[all-services.scan]]
|
||||||
|
name = 'sslscan'
|
||||||
|
command = 'if [ "{secure}" == "True" ]; then sslscan --show-certificate --no-colour {address}:{port} 2>&1 | tee "{scandir}/{protocol}_{port}_sslscan.txt"; fi'
|
||||||
|
|
||||||
|
[cassandra]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^apani1'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[cassandra.scan]]
|
||||||
|
name = 'nmap-cassandra'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(cassandra* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_cassandra_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_cassandra_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[cups]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^ipp'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[cups.scan]]
|
||||||
|
name = 'nmap-cups'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(cups* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_cups_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_cups_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[distcc]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^distccd'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[distcc.scan]]
|
||||||
|
name = 'nmap-distcc'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,distcc-cve2004-2687" --script-args="distcc-cve2004-2687.cmd=id" -oN "{scandir}/{protocol}_{port}_distcc_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_distcc_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[dns]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^domain'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[dns.scan]]
|
||||||
|
name = 'nmap-dns'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(dns* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_dns_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_dns_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[finger]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^finger'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[finger.scan]]
|
||||||
|
nmap = 'nmap-finger'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,finger" -oN "{scandir}/{protocol}_{port}_finger_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_finger_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[ftp]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^ftp',
|
||||||
|
'^ftp\-data'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[ftp.scan]]
|
||||||
|
name = 'nmap-ftp'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(ftp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_ftp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_ftp_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[[ftp.scan.pattern]]
|
||||||
|
description = 'Anonymous FTP Enabled!'
|
||||||
|
pattern = 'Anonymous FTP login allowed'
|
||||||
|
|
||||||
|
[[ftp.manual]]
|
||||||
|
description = 'Bruteforce logins:'
|
||||||
|
commands = [
|
||||||
|
'hydra -L "{username_wordlist}" -P "{password_wordlist}" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_ftp_hydra.txt" ftp://{address}',
|
||||||
|
'medusa -U "{username_wordlist}" -P "{password_wordlist}" -e ns -n {port} -O "{scandir}/{protocol}_{port}_ftp_medusa.txt" -M ftp -h {address}'
|
||||||
|
]
|
||||||
|
|
||||||
|
[http]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^http',
|
||||||
|
]
|
||||||
|
|
||||||
|
ignore-service-names = [
|
||||||
|
'^nacn_http$'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[http.scan]]
|
||||||
|
name = 'nmap-http'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(http* or ssl*) and not (brute or broadcast or dos or external or http-slowloris* or fuzzer)" -oN "{scandir}/{protocol}_{port}_http_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_{scheme}_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[[http.scan.pattern]]
|
||||||
|
description = 'Identified HTTP Server: {match}'
|
||||||
|
pattern = 'Server: ([^\n]+)'
|
||||||
|
|
||||||
|
[[http.scan.pattern]]
|
||||||
|
description = 'WebDAV is enabled'
|
||||||
|
pattern = 'WebDAV is ENABLED'
|
||||||
|
|
||||||
|
[[http.scan]]
|
||||||
|
name = 'curl-index'
|
||||||
|
command = 'curl -sSik {scheme}://{address}:{port}/ -m 10 2>&1 | tee "{scandir}/{protocol}_{port}_{scheme}_index.html"'
|
||||||
|
|
||||||
|
[[http.scan.pattern]]
|
||||||
|
pattern = '(?i)Powered by [^\n]+'
|
||||||
|
|
||||||
|
[[http.scan]]
|
||||||
|
name = 'curl-robots'
|
||||||
|
command = 'curl -sSik {scheme}://{address}:{port}/robots.txt -m 10 2>&1 | tee "{scandir}/{protocol}_{port}_{scheme}_robots.txt"'
|
||||||
|
|
||||||
|
[[http.scan]]
|
||||||
|
name = 'wkhtmltoimage'
|
||||||
|
command = 'if hash wkhtmltoimage 2> /dev/null; then wkhtmltoimage --format png {scheme}://{address}:{port}/ {scandir}/{protocol}_{port}_{scheme}_screenshot.png; fi'
|
||||||
|
|
||||||
|
[[http.scan]]
|
||||||
|
name = 'whatweb'
|
||||||
|
command = 'whatweb --color=never --no-errors -a 3 -v {scheme}://{address}:{port} 2>&1 | tee "{scandir}/{protocol}_{port}_{scheme}_whatweb.txt"'
|
||||||
|
|
||||||
|
[[http.scan]]
|
||||||
|
name = 'nikto'
|
||||||
|
command = 'nikto -ask=no -h {scheme}://{address}:{port} 2>&1 | tee "{scandir}/{protocol}_{port}_{scheme}_nikto.txt"'
|
||||||
|
|
||||||
|
[[http.scan]]
|
||||||
|
name = 'gobuster'
|
||||||
|
command = 'if [[ `gobuster -h 2>&1 | grep -F "mode (dir)"` ]]; then gobuster -u {scheme}://{address}:{port}/ -w /usr/share/seclists/Discovery/Web-Content/common.txt -e -k -l -s "200,204,301,302,307,401,403" -x "txt,html,php,asp,aspx,jsp" -o "{scandir}/{protocol}_{port}_{scheme}_gobuster.txt"; else gobuster dir -u {scheme}://{address}:{port}/ -w /usr/share/seclists/Discovery/Web-Content/common.txt -z -k -l -x "txt,html,php,asp,aspx,jsp" -o "{scandir}/{protocol}_{port}_{scheme}_gobuster.txt"; fi'
|
||||||
|
|
||||||
|
[[http.manual]]
|
||||||
|
description = '(dirsearch) Multi-threaded recursive directory/file enumeration for web servers using various wordlists:'
|
||||||
|
commands = [
|
||||||
|
'dirsearch -u {scheme}://{address}:{port}/ -t 16 -r -e txt,html,php,asp,aspx,jsp -f -w /usr/share/seclists/Discovery/Web-Content/big.txt --plain-text-report="{scandir}/{protocol}_{port}_{scheme}_dirsearch_big.txt"',
|
||||||
|
'dirsearch -u {scheme}://{address}:{port}/ -t 16 -r -e txt,html,php,asp,aspx,jsp -f -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt --plain-text-report="{scandir}/{protocol}_{port}_{scheme}_dirsearch_dirbuster.txt"'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[http.manual]]
|
||||||
|
description = '(dirb) Recursive directory/file enumeration for web servers using various wordlists (same as dirsearch above):'
|
||||||
|
commands = [
|
||||||
|
'dirb {scheme}://{address}:{port}/ /usr/share/seclists/Discovery/Web-Content/big.txt -l -r -S -X ",.txt,.html,.php,.asp,.aspx,.jsp" -o "{scandir}/{protocol}_{port}_{scheme}_dirb_big.txt"',
|
||||||
|
'dirb {scheme}://{address}:{port}/ /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -l -r -S -X ",.txt,.html,.php,.asp,.aspx,.jsp" -o "{scandir}/{protocol}_{port}_{scheme}_dirb_dirbuster.txt"'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[http.manual]]
|
||||||
|
description = '(gobuster v3) Directory/file enumeration for web servers using various wordlists (same as dirb above):'
|
||||||
|
commands = [
|
||||||
|
'gobuster dir -u {scheme}://{address}:{port}/ -w /usr/share/seclists/Discovery/Web-Content/big.txt -e -k -l -s "200,204,301,302,307,403,500" -x "txt,html,php,asp,aspx,jsp" -z -o "{scandir}/{protocol}_{port}_{scheme}_gobuster_big.txt"',
|
||||||
|
'gobuster dir -u {scheme}://{address}:{port}/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -e -k -l -s "200,204,301,302,307,403,500" -x "txt,html,php,asp,aspx,jsp" -z -o "{scandir}/{protocol}_{port}_{scheme}_gobuster_dirbuster.txt"'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[http.manual]]
|
||||||
|
description = '(gobuster v1 & v2) Directory/file enumeration for web servers using various wordlists (same as dirb above):'
|
||||||
|
commands = [
|
||||||
|
'gobuster -u {scheme}://{address}:{port}/ -w /usr/share/seclists/Discovery/Web-Content/big.txt -e -k -l -s "200,204,301,302,307,403,500" -x "txt,html,php,asp,aspx,jsp" -o "{scandir}/{protocol}_{port}_{scheme}_gobuster_big.txt"',
|
||||||
|
'gobuster -u {scheme}://{address}:{port}/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -e -k -l -s "200,204,301,302,307,403,500" -x "txt,html,php,asp,aspx,jsp" -o "{scandir}/{protocol}_{port}_{scheme}_gobuster_dirbuster.txt"'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[http.manual]]
|
||||||
|
description = '(wpscan) WordPress Security Scanner (useful if WordPress is found):'
|
||||||
|
commands = [
|
||||||
|
'wpscan --url {scheme}://{address}:{port}/ --no-update -e vp,vt,tt,cb,dbe,u,m --plugins-detection aggressive --plugins-version-detection aggressive -f cli-no-color 2>&1 | tee "{scandir}/{protocol}_{port}_{scheme}_wpscan.txt"'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[http.manual]]
|
||||||
|
description = "Credential bruteforcing commands (don't run these without modifying them):"
|
||||||
|
commands = [
|
||||||
|
'hydra -L "{username_wordlist}" -P "{password_wordlist}" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_{scheme}_auth_hydra.txt" {scheme}-get://{address}/path/to/auth/area',
|
||||||
|
'medusa -U "{username_wordlist}" -P "{password_wordlist}" -e ns -n {port} -O "{scandir}/{protocol}_{port}_{scheme}_auth_medusa.txt" -M http -h {address} -m DIR:/path/to/auth/area',
|
||||||
|
'hydra -L "{username_wordlist}" -P "{password_wordlist}" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_{scheme}_form_hydra.txt" {scheme}-post-form://{address}/path/to/login.php:username=^USER^&password=^PASS^:invalid-login-message',
|
||||||
|
'medusa -U "{username_wordlist}" -P "{password_wordlist}" -e ns -n {port} -O "{scandir}/{protocol}_{port}_{scheme}_form_medusa.txt" -M web-form -h {address} -m FORM:/path/to/login.php -m FORM-DATA:"post?username=&password=" -m DENY-SIGNAL:"invalid login message"',
|
||||||
|
]
|
||||||
|
|
||||||
|
[imap]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^imap'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[imap.scan]]
|
||||||
|
name = 'nmap-imap'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(imap* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_imap_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_imap_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[kerberos]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^kerberos',
|
||||||
|
'^kpasswd'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[kerberos.scan]]
|
||||||
|
name = 'nmap-kerberos'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,krb5-enum-users" -oN "{scandir}/{protocol}_{port}_kerberos_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_kerberos_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[ldap]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^ldap'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[ldap.scan]]
|
||||||
|
name = 'nmap-ldap'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(ldap* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_ldap_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_ldap_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[[ldap.scan]]
|
||||||
|
name = 'enum4linux'
|
||||||
|
command = 'enum4linux -a -M -l -d {address} 2>&1 | tee "{scandir}/enum4linux.txt"'
|
||||||
|
run_once = true
|
||||||
|
ports.tcp = [139, 389, 445]
|
||||||
|
ports.udp = [137]
|
||||||
|
|
||||||
|
[[ldap.manual]]
|
||||||
|
description = 'ldapsearch command (modify before running)'
|
||||||
|
commands = [
|
||||||
|
'ldapsearch -x -D "<username>" -w "<password>"" -p {port} -h {address} -b "dc=example,dc=com" -s sub "(objectclass=*) 2>&1 | tee > "{scandir}/{protocol}_{port}_ldap_all-entries.txt"'
|
||||||
|
]
|
||||||
|
|
||||||
|
[mongodb]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^mongod'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[mongodb.scan]]
|
||||||
|
name = 'nmap-mongodb'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(mongodb* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_mongodb_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_mongodb_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[mssql]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^mssql',
|
||||||
|
'^ms\-sql'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[mssql.scan]]
|
||||||
|
name = 'nmap-mssql'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(ms-sql* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" --script-args="mssql.instance-port={port},mssql.username=sa,mssql.password=sa" -oN "{scandir}/{protocol}_{port}_mssql_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_mssql_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[[mssql.manual]]
|
||||||
|
description = '(sqsh) interactive database shell'
|
||||||
|
commands = [
|
||||||
|
'sqsh -U <username> -P <password> -S {address}:{port}'
|
||||||
|
]
|
||||||
|
|
||||||
|
[mysql]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^mysql'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[mysql.scan]]
|
||||||
|
name = 'nmap-mysql'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(mysql* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_mysql_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_mysql_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[nfs]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^nfs',
|
||||||
|
'^rpcbind'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[nfs.scan]]
|
||||||
|
name = 'nmap-nfs'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(rpcinfo or nfs*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_nfs_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_nfs_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[[nfs.scan]]
|
||||||
|
name = 'showmount'
|
||||||
|
command = 'showmount -e {address} 2>&1 | tee "{scandir}/{protocol}_{port}_showmount.txt"'
|
||||||
|
|
||||||
|
[nntp]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^nntp'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[nntp.scan]]
|
||||||
|
name = 'nmap-nntp'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,nntp-ntlm-info" -oN "{scandir}/{protocol}_{port}_nntp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_nntp_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[oracle]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^oracle'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[oracle.scan]]
|
||||||
|
name = 'nmap-oracle'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(oracle* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_oracle_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_oracle_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[[oracle.scan]]
|
||||||
|
name = 'oracle-tnscmd-ping'
|
||||||
|
command = 'tnscmd10g ping -h {address} -p {port} 2>&1 | tee "{scandir}/{protocol}_{port}_oracle_tnscmd_ping.txt"'
|
||||||
|
|
||||||
|
[[oracle.scan]]
|
||||||
|
name = 'oracle-tnscmd-version'
|
||||||
|
command = 'tnscmd10g version -h {address} -p {port} 2>&1 | tee "{scandir}/{protocol}_{port}_oracle_tnscmd_version.txt"'
|
||||||
|
|
||||||
|
[[oracle.scan]]
|
||||||
|
name = 'oracle-tnscmd-version'
|
||||||
|
command = 'tnscmd10g version -h {address} -p {port} 2>&1 | tee "{scandir}/{protocol}_{port}_oracle_tnscmd_version.txt"'
|
||||||
|
|
||||||
|
[[oracle.scan]]
|
||||||
|
name = 'oracle-scanner'
|
||||||
|
command = 'oscanner -v -s {address} -P {port} 2>&1 | tee "{scandir}/{protocol}_{port}_oracle_scanner.txt"'
|
||||||
|
|
||||||
|
[[oracle.manual]]
|
||||||
|
description = 'Brute-force SIDs using Nmap'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,oracle-sid-brute" -oN "{scandir}/{protocol}_{port}_oracle_sid-brute_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_oracle_sid-brute_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[[oracle.manual]]
|
||||||
|
description = 'Install ODAT (https://github.com/quentinhardy/odat) and run the following commands:'
|
||||||
|
commands = [
|
||||||
|
'python odat.py tnscmd -s {address} -p {port} --ping',
|
||||||
|
'python odat.py tnscmd -s {address} -p {port} --version',
|
||||||
|
'python odat.py tnscmd -s {address} -p {port} --status',
|
||||||
|
'python odat.py sidguesser -s {address} -p {port}',
|
||||||
|
'python odat.py passwordguesser -s {address} -p {port} -d <sid> --accounts-file accounts/accounts_multiple.txt',
|
||||||
|
'python odat.py tnspoison -s {address} -p {port} -d <sid> --test-module'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[oracle.manual]]
|
||||||
|
description = 'Install Oracle Instant Client (https://github.com/rapid7/metasploit-framework/wiki/How-to-get-Oracle-Support-working-with-Kali-Linux) and then bruteforce with patator:'
|
||||||
|
commands = [
|
||||||
|
'patator oracle_login host={address} port={port} user=COMBO00 password=COMBO01 0=/usr/share/seclists/Passwords/Default-Credentials/oracle-betterdefaultpasslist.txt -x ignore:code=ORA-01017 -x ignore:code=ORA-28000'
|
||||||
|
]
|
||||||
|
|
||||||
|
[pop3]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^pop3'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[pop3.scan]]
|
||||||
|
name = 'nmap-pop3'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(pop3* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_pop3_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_pop3_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[rdp]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^rdp',
|
||||||
|
'^ms\-wbt\-server',
|
||||||
|
'^ms\-term\-serv'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rdp.scan]]
|
||||||
|
name = 'nmap-rdp'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(rdp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_rdp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_rdp_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[[rdp.manual]]
|
||||||
|
description = 'Bruteforce logins:'
|
||||||
|
commands = [
|
||||||
|
'hydra -L "{username_wordlist}" -P "{password_wordlist}" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_rdp_hydra.txt" rdp://{address}',
|
||||||
|
'medusa -U "{username_wordlist}" -P "{password_wordlist}" -e ns -n {port} -O "{scandir}/{protocol}_{port}_rdp_medusa.txt" -M rdp -h {address}'
|
||||||
|
]
|
||||||
|
|
||||||
|
[rmi]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^java\-rmi',
|
||||||
|
'^rmiregistry'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rmi.scan]]
|
||||||
|
name = 'nmap-rmi'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,rmi-vuln-classloader,rmi-dumpregistry" -oN "{scandir}/{protocol}_{port}_rmi_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_rmi_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[rpc]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^msrpc',
|
||||||
|
'^rpcbind',
|
||||||
|
'^erpc'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[rpc.scan]]
|
||||||
|
name = 'nmap-msrpc'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,msrpc-enum,rpc-grind,rpcinfo" -oN "{scandir}/{protocol}_{port}_rpc_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_rpc_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[[rpc.manual]]
|
||||||
|
description = 'RPC Client:'
|
||||||
|
commands = [
|
||||||
|
'rpcclient -p {port} -U "" {address}'
|
||||||
|
]
|
||||||
|
|
||||||
|
[sip]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^asterisk'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[sip.scan]]
|
||||||
|
name = 'nmap-sip'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,sip-enum-users,sip-methods" -oN "{scandir}/{protocol}_{port}_sip_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_sip_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[[sip.scan]]
|
||||||
|
name = 'svwar'
|
||||||
|
command = 'svwar -D -m INVITE -p {port} {address}'
|
||||||
|
|
||||||
|
[ssh]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^ssh'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[ssh.scan]]
|
||||||
|
name = 'nmap-ssh'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,ssh2-enum-algos,ssh-hostkey,ssh-auth-methods" -oN "{scandir}/{protocol}_{port}_ssh_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_ssh_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[[ssh.manual]]
|
||||||
|
description = 'Bruteforce logins:'
|
||||||
|
commands = [
|
||||||
|
'hydra -L "{username_wordlist}" -P "{password_wordlist}" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_ssh_hydra.txt" ssh://{address}',
|
||||||
|
'medusa -U "{username_wordlist}" -P "{password_wordlist}" -e ns -n {port} -O "{scandir}/{protocol}_{port}_ssh_medusa.txt" -M ssh -h {address}'
|
||||||
|
]
|
||||||
|
[smb]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^smb',
|
||||||
|
'^microsoft\-ds',
|
||||||
|
'^netbios'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[smb.scan]]
|
||||||
|
name = 'nmap-smb'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(nbstat or smb* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[[smb.scan]]
|
||||||
|
name = 'enum4linux'
|
||||||
|
command = 'enum4linux -a -M -l -d {address} 2>&1 | tee "{scandir}/enum4linux.txt"'
|
||||||
|
run_once = true
|
||||||
|
ports.tcp = [139, 389, 445]
|
||||||
|
ports.udp = [137]
|
||||||
|
|
||||||
|
[[smb.scan]]
|
||||||
|
name = 'nbtscan'
|
||||||
|
command = 'nbtscan -rvh {address} 2>&1 | tee "{scandir}/nbtscan.txt"'
|
||||||
|
run_once = true
|
||||||
|
ports.udp = [137]
|
||||||
|
|
||||||
|
[[smb.scan]]
|
||||||
|
name = 'smbclient'
|
||||||
|
command = 'smbclient -L\\ -N -I {address} 2>&1 | tee "{scandir}/smbclient.txt"'
|
||||||
|
run_once = true
|
||||||
|
ports.tcp = [139, 445]
|
||||||
|
|
||||||
|
[[smb.scan]]
|
||||||
|
name = 'smbmap-share-permissions'
|
||||||
|
command = 'smbmap -H {address} -P {port} 2>&1 | tee -a "{scandir}/smbmap-share-permissions.txt"; smbmap -u null -p "" -H {address} -P {port} 2>&1 | tee -a "{scandir}/smbmap-share-permissions.txt"'
|
||||||
|
|
||||||
|
[[smb.scan]]
|
||||||
|
name = 'smbmap-list-contents'
|
||||||
|
command = 'smbmap -H {address} -P {port} -R 2>&1 | tee -a "{scandir}/smbmap-list-contents.txt"; smbmap -u null -p "" -H {address} -P {port} -R 2>&1 | tee -a "{scandir}/smbmap-list-contents.txt"'
|
||||||
|
|
||||||
|
[[smb.scan]]
|
||||||
|
name = 'smbmap-execute-command'
|
||||||
|
command = 'smbmap -H {address} -P {port} -x "ipconfig /all" 2>&1 | tee -a "{scandir}/smbmap-execute-command.txt"; smbmap -u null -p "" -H {address} -P {port} -x "ipconfig /all" 2>&1 | tee -a "{scandir}/smbmap-execute-command.txt"'
|
||||||
|
|
||||||
|
[[smb.manual]]
|
||||||
|
description = 'Nmap scans for SMB vulnerabilities that could potentially cause a DoS if scanned (according to Nmap). Be careful:'
|
||||||
|
commands = [
|
||||||
|
'nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms06-025" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_ms06-025.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms06-025.xml" {address}',
|
||||||
|
'nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms07-029" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_ms07-029.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms07-029.xml" {address}',
|
||||||
|
'nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms08-067" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_ms08-067.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms08-067.xml" {address}'
|
||||||
|
]
|
||||||
|
|
||||||
|
[smtp]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^smtp'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[smtp.scan]]
|
||||||
|
name = 'nmap-smtp'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(smtp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_smtp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_smtp_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[[smtp.scan]]
|
||||||
|
name = 'smtp-user-enum'
|
||||||
|
command = 'smtp-user-enum -M VRFY -U "{username_wordlist}" -t {address} -p {port} 2>&1 | tee "{scandir}/{protocol}_{port}_smtp_user-enum.txt"'
|
||||||
|
|
||||||
|
[snmp]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^snmp'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[snmp.scan]]
|
||||||
|
name = 'nmap-snmp'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(snmp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_snmp-nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_snmp_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[[snmp.scan]]
|
||||||
|
name = 'onesixtyone'
|
||||||
|
command = 'onesixtyone -c /usr/share/seclists/Discovery/SNMP/common-snmp-community-strings-onesixtyone.txt -dd {address} 2>&1 | tee "{scandir}/{protocol}_{port}_snmp_onesixtyone.txt"'
|
||||||
|
run_once = true
|
||||||
|
ports.udp = [161]
|
||||||
|
|
||||||
|
[[snmp.scan]]
|
||||||
|
name = 'snmpwalk'
|
||||||
|
command = 'snmpwalk -c public -v 1 {address} 2>&1 | tee "{scandir}/{protocol}_{port}_snmp_snmpwalk.txt"'
|
||||||
|
run_once = true
|
||||||
|
ports.udp = [161]
|
||||||
|
|
||||||
|
[[snmp.scan]]
|
||||||
|
name = 'snmpwalk-system-processes'
|
||||||
|
command = 'snmpwalk -c public -v 1 {address} 1.3.6.1.2.1.25.1.6.0 2>&1 | tee "{scandir}/{protocol}_{port}_snmp_snmpwalk_system_processes.txt"'
|
||||||
|
run_once = true
|
||||||
|
ports.udp = [161]
|
||||||
|
|
||||||
|
[[snmp.scan]]
|
||||||
|
name = 'snmpwalk-running-processes'
|
||||||
|
command = 'snmpwalk -c public -v 1 {address} 1.3.6.1.2.1.25.4.2.1.2 2>&1 | tee "{scandir}/{protocol}_{port}_snmp_snmpwalk_running_processes.txt"'
|
||||||
|
run_once = true
|
||||||
|
ports.udp = [161]
|
||||||
|
|
||||||
|
[[snmp.scan]]
|
||||||
|
name = 'snmpwalk-process-paths'
|
||||||
|
command = 'snmpwalk -c public -v 1 {address} 1.3.6.1.2.1.25.4.2.1.4 2>&1 | tee "{scandir}/{protocol}_{port}_snmp_snmpwalk_process_paths.txt"'
|
||||||
|
run_once = true
|
||||||
|
ports.udp = [161]
|
||||||
|
|
||||||
|
[[snmp.scan]]
|
||||||
|
name = 'snmpwalk-storage-units'
|
||||||
|
command = 'snmpwalk -c public -v 1 {address} 1.3.6.1.2.1.25.2.3.1.4 2>&1 | tee "{scandir}/{protocol}_{port}_snmp_snmpwalk_storage_units.txt"'
|
||||||
|
run_once = true
|
||||||
|
ports.udp = [161]
|
||||||
|
|
||||||
|
[[snmp.scan]]
|
||||||
|
name = 'snmpwalk-software-names'
|
||||||
|
command = 'snmpwalk -c public -v 1 {address} 1.3.6.1.2.1.25.6.3.1.2 2>&1 | tee "{scandir}/{protocol}_{port}_snmp_snmpwalk_software_names.txt"'
|
||||||
|
run_once = true
|
||||||
|
ports.udp = [161]
|
||||||
|
|
||||||
|
[[snmp.scan]]
|
||||||
|
name = 'snmpwalk-user-accounts'
|
||||||
|
command = 'snmpwalk -c public -v 1 {address} 1.3.6.1.4.1.77.1.2.25 2>&1 | tee "{scandir}/{protocol}_{port}_snmp_snmpwalk_user_accounts.txt"'
|
||||||
|
run_once = true
|
||||||
|
ports.udp = [161]
|
||||||
|
|
||||||
|
[[snmp.scan]]
|
||||||
|
name = 'snmpwalk-tcp-ports'
|
||||||
|
command = 'snmpwalk -c public -v 1 {address} 1.3.6.1.2.1.6.13.1.3 2>&1 | tee "{scandir}/{protocol}_{port}_snmp_snmpwalk_tcp_ports.txt"'
|
||||||
|
run_once = true
|
||||||
|
ports.udp = [161]
|
||||||
|
|
||||||
|
[telnet]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^telnet'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[telnet.scan]]
|
||||||
|
name = 'nmap-telnet'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,telnet-encryption,telnet-ntlm-info" -oN "{scandir}/{protocol}_{port}_telnet-nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_telnet_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[tftp]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^tftp'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[tftp.scan]]
|
||||||
|
name = 'nmap-tftp'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,tftp-enum" -oN "{scandir}/{protocol}_{port}_tftp-nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_tftp_nmap.xml" {address}'
|
||||||
|
|
||||||
|
[vnc]
|
||||||
|
|
||||||
|
service-names = [
|
||||||
|
'^vnc'
|
||||||
|
]
|
||||||
|
|
||||||
|
[[vnc.scan]]
|
||||||
|
name = 'nmap-vnc'
|
||||||
|
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(vnc* or realvnc* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_vnc_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_vnc_nmap.xml" {address}'
|
101
portscan.py
Normal file
101
portscan.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
from importlib import util
|
||||||
|
|
||||||
|
threading_spec = util.find_spec("threading")
|
||||||
|
queue_spec = util.find_spec("queue")
|
||||||
|
|
||||||
|
if threading_spec is not None:
|
||||||
|
import threading
|
||||||
|
import queue
|
||||||
|
NUM_THREADS = 10
|
||||||
|
THREADING_ENABLED = True
|
||||||
|
QUEUE = queue.Queue()
|
||||||
|
else:
|
||||||
|
THREADING_ENABLED = False
|
||||||
|
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage: %s <host> [ports] [num_threads]")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
host = sys.argv[1]
|
||||||
|
ports = range(1,1001)
|
||||||
|
|
||||||
|
if len(sys.argv) >= 3:
|
||||||
|
ports_param = sys.argv[2]
|
||||||
|
pattern = re.compile("^(\\d)+(-(\\d+)?)?$")
|
||||||
|
m = pattern.match(ports_param)
|
||||||
|
if m is None:
|
||||||
|
print("Invalid port range")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
start_port = int(m.group(1))
|
||||||
|
end_port = start_port
|
||||||
|
if m.group(2) is not None:
|
||||||
|
if m.group(3) is None:
|
||||||
|
end_port = 65535
|
||||||
|
else:
|
||||||
|
end_port = int(m.group(3))
|
||||||
|
|
||||||
|
if start_port < 1 or start_port > 65535:
|
||||||
|
print("Invalid start port")
|
||||||
|
exit(1)
|
||||||
|
elif end_port < 1 or end_port > 65535:
|
||||||
|
print("Invalid end port")
|
||||||
|
exit(1)
|
||||||
|
elif start_port > end_port:
|
||||||
|
print("Invalid port range")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
ports = range(start_port, end_port+1)
|
||||||
|
|
||||||
|
if len(sys.argv) >= 4:
|
||||||
|
if not THREADING_ENABLED:
|
||||||
|
print("Threading is not supported by this system, you need the libraries: threading, queue")
|
||||||
|
exit(1)
|
||||||
|
else:
|
||||||
|
NUM_THREADS = int(sys.argv[3])
|
||||||
|
if NUM_THREADS < 1:
|
||||||
|
print("Invalid thread count:", NUM_THREADS)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
def tryConnect(host, port):
|
||||||
|
try:
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
sock.settimeout(3.0)
|
||||||
|
sock.connect((host,port))
|
||||||
|
sock.close()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def doWork(q, host):
|
||||||
|
while not q.empty():
|
||||||
|
p = q.get()
|
||||||
|
if tryConnect(host, p):
|
||||||
|
print("[+] Port %d is open" % p)
|
||||||
|
|
||||||
|
if not THREADING_ENABLED:
|
||||||
|
print("Scanning ports: %d-%d..." % (ports[0], ports[len(ports)-1]))
|
||||||
|
open_ports = []
|
||||||
|
for p in ports:
|
||||||
|
if tryConnect(host, p):
|
||||||
|
print("[+] Port %d is open" % p)
|
||||||
|
print("Done")
|
||||||
|
else:
|
||||||
|
print("Scanning ports: %d-%d with %d threads..." % (ports[0], ports[len(ports)-1], NUM_THREADS))
|
||||||
|
for i in ports:
|
||||||
|
QUEUE.put(i)
|
||||||
|
|
||||||
|
threads = []
|
||||||
|
for i in range(NUM_THREADS):
|
||||||
|
t = threading.Thread(target=doWork, args=(QUEUE, host))
|
||||||
|
t.start()
|
||||||
|
threads.append(t)
|
||||||
|
|
||||||
|
for t in threads:
|
||||||
|
t.join()
|
||||||
|
print("Done")
|
Loading…
Reference in New Issue
Block a user