Added --single-target and --only-scans-dir options.

This commit is contained in:
Tib3rius 2019-09-12 14:34:24 -04:00
parent d7058848a5
commit 4d79a9e340
1 changed files with 94 additions and 27 deletions

View File

@ -17,6 +17,7 @@ import re
import socket import socket
import string import string
import sys import sys
import time
import toml import toml
verbose = 0 verbose = 0
@ -33,6 +34,9 @@ password_wordlist = '/usr/share/seclists/Passwords/darkweb2017-top100.txt'
rootdir = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) rootdir = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
single_target = False
only_scans_dir = False
def e(*args, frame_index=1, **kvargs): def e(*args, frame_index=1, **kvargs):
frame = sys._getframe(frame_index) frame = sys._getframe(frame_index)
@ -104,22 +108,61 @@ 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) cprint(*args, color=Fore.RED, char='!', sep=sep, end=end, file=file, frame_index=2, **kvargs)
exit(-1) exit(-1)
def merge_toml(a, b, path=None):
if path is None: path = []
for key in b:
if key in a:
if isinstance(a[key], dict) and isinstance(b[key], dict):
merge_toml(a[key], b[key], path + [str(key)])
elif a[key] == b[key]:
pass
else:
a[key] = b[key]
else:
a[key] = b[key]
return a
port_scan_profiles_config_file = 'port-scan-profiles.toml' 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:
try: with open(os.path.join(rootdir, 'config', port_scan_profiles_config_file), 'r') as p:
port_scan_profiles_config = toml.load(p) try:
port_scan_profiles_config = toml.load(p)
if len(port_scan_profiles_config) == 0: custom_port_scan_profiles_config_file = os.path.join(rootdir, 'config', 'custom-port-scan-profiles.toml')
fail('There do not appear to be any port scan profiles configured in the {port_scan_profiles_config_file} config file.') if os.path.isfile(custom_port_scan_profiles_config_file):
try:
with open(custom_port_scan_profiles_config_file, 'r') as c:
custom_port_scan_profiles_config = toml.load(c)
merge_toml(port_scan_profiles_config, custom_port_scan_profiles_config)
except IOError:
fail('Error: Couldn\'t load custom-port-scan-profiles.toml config file. Check the file exists and has the correct permissions.')
except toml.decoder.TomlDecodeError as e: if len(port_scan_profiles_config) == 0:
fail('Error: Couldn\'t parse {port_scan_profiles_config_file} config file. Check syntax and duplicate tags.') fail('There do not appear to be any port scan profiles configured in the {port_scan_profiles_config_file} config file.')
with open(os.path.join(rootdir, 'config', 'service-scans.toml'), 'r') as c: except toml.decoder.TomlDecodeError as e:
try: fail('Error: Couldn\'t parse {port_scan_profiles_config_file} or custom-port-scan-profiles.toml config file. Check syntax and duplicate tags.')
service_scans_config = toml.load(c) except IOError:
except toml.decoder.TomlDecodeError as e: fail('Error: Couldn\'t load {port_scan_profiles_config_file} config file. Check the file exists and has the correct permissions.')
fail('Error: Couldn\'t parse service-scans.toml config file. Check syntax and duplicate tags.')
try:
with open(os.path.join(rootdir, 'config', 'service-scans.toml'), 'r') as s:
try:
service_scans_config = toml.load(s)
custom_service_scans_config_file = os.path.join(rootdir, 'config', 'custom-service-scans.toml')
if os.path.isfile(custom_service_scans_config_file):
try:
with open(custom_service_scans_config_file, 'r') as c:
custom_service_scans_config = toml.load(c)
merge_toml(service_scans_config, custom_service_scans_config)
except IOError:
fail('Error: Couldn\'t load custom-service-scans.toml config file. Check the file exists and has the correct permissions.')
except toml.decoder.TomlDecodeError as e:
fail('Error: Couldn\'t parse service-scans.toml or custom-service-scans.toml config file. Check syntax and duplicate tags.')
except IOError:
fail('Error: Couldn\'t load service-scans.toml config file. Check the file exists and has the correct permissions.')
with open(os.path.join(rootdir, 'config', 'global-patterns.toml'), 'r') as p: with open(os.path.join(rootdir, 'config', 'global-patterns.toml'), 'r') as p:
try: try:
@ -401,8 +444,9 @@ async def scan_services(loop, semaphore, target):
info('Found {bmagenta}{service}{rst} on {bmagenta}{protocol}/{port}{rst} on target {byellow}{address}{rst}') info('Found {bmagenta}{service}{rst} on {bmagenta}{protocol}/{port}{rst} on target {byellow}{address}{rst}')
with open(os.path.join(target.reportdir, 'notes.txt'), 'a') as file: if not only_scans_dir:
file.writelines(e('[*] {service} found on {protocol}/{port}.\n\n\n\n')) 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': if protocol == 'udp':
nmap_extra = nmap + " -sU" nmap_extra = nmap + " -sU"
@ -514,24 +558,32 @@ async def scan_services(loop, semaphore, target):
pending.add(asyncio.ensure_future(run_cmd(semaphore, e(command), target, tag=tag, patterns=patterns))) pending.add(asyncio.ensure_future(run_cmd(semaphore, e(command), target, tag=tag, patterns=patterns)))
def scan_host(target, concurrent_scans): def scan_host(target, concurrent_scans):
start_time = time.time()
info('Scanning target {byellow}{target.address}{rst}') info('Scanning target {byellow}{target.address}{rst}')
basedir = os.path.abspath(os.path.join(outdir, target.address + srvname)) if single_target:
basedir = os.path.abspath(outdir)
else:
basedir = os.path.abspath(os.path.join(outdir, target.address + srvname))
target.basedir = basedir target.basedir = basedir
os.makedirs(basedir, exist_ok=True) os.makedirs(basedir, exist_ok=True)
exploitdir = os.path.abspath(os.path.join(basedir, 'exploit')) if not only_scans_dir:
os.makedirs(exploitdir, exist_ok=True) exploitdir = os.path.abspath(os.path.join(basedir, 'exploit'))
os.makedirs(exploitdir, exist_ok=True)
lootdir = os.path.abspath(os.path.join(basedir, 'loot')) lootdir = os.path.abspath(os.path.join(basedir, 'loot'))
os.makedirs(lootdir, exist_ok=True) os.makedirs(lootdir, exist_ok=True)
reportdir = os.path.abspath(os.path.join(basedir, 'report')) reportdir = os.path.abspath(os.path.join(basedir, 'report'))
target.reportdir = reportdir target.reportdir = reportdir
os.makedirs(reportdir, exist_ok=True) os.makedirs(reportdir, exist_ok=True)
screenshotdir = os.path.abspath(os.path.join(reportdir, 'screenshots')) open(os.path.abspath(os.path.join(reportdir, 'local.txt')), 'a').close()
os.makedirs(screenshotdir, exist_ok=True) 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')) scandir = os.path.abspath(os.path.join(basedir, 'scans'))
target.scandir = scandir target.scandir = scandir
@ -539,9 +591,6 @@ def scan_host(target, concurrent_scans):
os.makedirs(os.path.abspath(os.path.join(scandir, 'xml')), exist_ok=True) os.makedirs(os.path.abspath(os.path.join(scandir, 'xml')), 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()
# Use a lock when writing to specific files that may be written to by other asynchronous functions. # Use a lock when writing to specific files that may be written to by other asynchronous functions.
target.lock = asyncio.Lock() target.lock = asyncio.Lock()
@ -553,6 +602,15 @@ def scan_host(target, concurrent_scans):
try: try:
loop.run_until_complete(scan_services(loop, semaphore, target)) loop.run_until_complete(scan_services(loop, semaphore, target))
elapsed_seconds = time.time() - start_time
m, s = divmod(elapsed_seconds, 60)
h, m = divmod(m, 60)
elapsed_time = ''
if h > 0:
elapsed_time += h + ' hour'
info('Finished scanning target {byellow}{target.address}{rst}') info('Finished scanning target {byellow}{target.address}{rst}')
except KeyboardInterrupt: except KeyboardInterrupt:
sys.exit(1) sys.exit(1)
@ -575,6 +633,8 @@ if __name__ == '__main__':
parser.add_argument('--profile', action='store', default='default', help='The port scanning profile to use (defined in port-scan-profiles.toml). Default: %(default)s') parser.add_argument('--profile', action='store', default='default', 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', help='The output directory for results. Default: %(default)s') parser.add_argument('-o', '--output', action='store', default='results', help='The output directory for results. Default: %(default)s')
nmap_group = parser.add_mutually_exclusive_group() nmap_group = parser.add_mutually_exclusive_group()
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.')
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', 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.') 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('-v', '--verbose', action='count', default=0, help='Enable verbose output. Repeat for more verbosity.')
@ -582,6 +642,9 @@ if __name__ == '__main__':
parser.error = lambda s: fail(s[0].upper() + s[1:]) parser.error = lambda s: fail(s[0].upper() + s[1:])
args = parser.parse_args() args = parser.parse_args()
single_target = args.single_target
only_scans_dir = args.only_scans_dir
errors = False errors = False
if args.concurrent_targets <= 0: if args.concurrent_targets <= 0:
@ -651,6 +714,10 @@ if __name__ == '__main__':
error('You must specify at least one target to scan!') error('You must specify at least one target to scan!')
errors = True errors = True
if single_target and len(args.targets) != 1:
error('You cannot provide more than one target when scanning in single-target mode.')
sys.exit(1)
targets = [] targets = []
for target in args.targets: for target in args.targets: