mirror of
https://github.com/smicallef/spiderfoot.git
synced 2026-10-12 02:22:47 +00:00
635 lines
24 KiB
Python
635 lines
24 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
# -------------------------------------------------------------------------------
|
|
# Name: sf
|
|
# Purpose: Main wrapper for calling all SpiderFoot modules
|
|
#
|
|
# Author: Steve Micallef <[email protected]>
|
|
#
|
|
# Created: 03/04/2012
|
|
# Copyright: (c) Steve Micallef 2012
|
|
# Licence: MIT
|
|
# -------------------------------------------------------------------------------
|
|
|
|
import argparse
|
|
import logging
|
|
import multiprocessing as mp
|
|
import os
|
|
import os.path
|
|
import random
|
|
import signal
|
|
import sys
|
|
import time
|
|
from copy import deepcopy
|
|
|
|
import cherrypy
|
|
import cherrypy_cors
|
|
from cherrypy.lib import auth_digest
|
|
|
|
from sflib import SpiderFoot
|
|
from sfscan import startSpiderFootScanner
|
|
from sfwebui import SpiderFootWebUi
|
|
from spiderfoot import SpiderFootHelpers
|
|
from spiderfoot import SpiderFootDb
|
|
from spiderfoot import SpiderFootCorrelator
|
|
from spiderfoot.logger import logListenerSetup, logWorkerSetup
|
|
from spiderfoot import __version__
|
|
|
|
scanId = None
|
|
dbh = None
|
|
|
|
|
|
def main() -> None:
|
|
# web server config
|
|
sfWebUiConfig = {
|
|
'host': '127.0.0.1',
|
|
'port': 5001,
|
|
'root': '/',
|
|
'cors_origins': [],
|
|
}
|
|
|
|
# 'Global' configuration options
|
|
# These can be overriden on a per-module basis, and some will
|
|
# be overridden from saved configuration settings stored in the DB.
|
|
sfConfig = {
|
|
'_debug': False, # Debug
|
|
'_maxthreads': 3, # Number of modules to run concurrently
|
|
'__logging': True, # Logging in general
|
|
'__outputfilter': None, # Event types to filter from modules' output
|
|
'_useragent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0', # User-Agent to use for HTTP requests
|
|
'_dnsserver': '', # Override the default resolver
|
|
'_fetchtimeout': 5, # number of seconds before giving up on a fetch
|
|
'_internettlds': 'https://publicsuffix.org/list/effective_tld_names.dat',
|
|
'_internettlds_cache': 72,
|
|
'_genericusers': ",".join(SpiderFootHelpers.usernamesFromWordlists(['generic-usernames'])),
|
|
'__database': f"{SpiderFootHelpers.dataPath()}/spiderfoot.db",
|
|
'__modules__': None, # List of modules. Will be set after start-up.
|
|
'__correlationrules__': None, # List of correlation rules. Will be set after start-up.
|
|
'_socks1type': '',
|
|
'_socks2addr': '',
|
|
'_socks3port': '',
|
|
'_socks4user': '',
|
|
'_socks5pwd': '',
|
|
}
|
|
|
|
sfOptdescs = {
|
|
'_debug': "Enable debugging?",
|
|
'_maxthreads': "Max number of modules to run concurrently",
|
|
'_useragent': "User-Agent string to use for HTTP requests. Prefix with an '@' to randomly select the User Agent from a file containing user agent strings for each request, e.g. @C:\\useragents.txt or @/home/bob/useragents.txt. Or supply a URL to load the list from there.",
|
|
'_dnsserver': "Override the default resolver with another DNS server. For example, 8.8.8.8 is Google's open DNS server.",
|
|
'_fetchtimeout': "Number of seconds before giving up on a HTTP request.",
|
|
'_internettlds': "List of Internet TLDs.",
|
|
'_internettlds_cache': "Hours to cache the Internet TLD list. This can safely be quite a long time given that the list doesn't change too often.",
|
|
'_genericusers': "List of usernames that if found as usernames or as part of e-mail addresses, should be treated differently to non-generics.",
|
|
'_socks1type': "SOCKS Server Type. Can be '4', '5', 'HTTP' or 'TOR'",
|
|
'_socks2addr': 'SOCKS Server IP Address.',
|
|
'_socks3port': 'SOCKS Server TCP Port. Usually 1080 for 4/5, 8080 for HTTP and 9050 for TOR.',
|
|
'_socks4user': 'SOCKS Username. Valid only for SOCKS4 and SOCKS5 servers.',
|
|
'_socks5pwd': "SOCKS Password. Valid only for SOCKS5 servers.",
|
|
'_modulesenabled': "Modules enabled for the scan." # This is a hack to get a description for an option not actually available.
|
|
}
|
|
|
|
# Legacy way to run the server
|
|
args = None
|
|
p = argparse.ArgumentParser(description=f"SpiderFoot {__version__}: Open Source Intelligence Automation.")
|
|
p.add_argument("-d", "--debug", action='store_true', help="Enable debug output.")
|
|
p.add_argument("-l", metavar="IP:port", help="IP and port to listen on.")
|
|
p.add_argument("-m", metavar="mod1,mod2,...", type=str, help="Modules to enable.")
|
|
p.add_argument("-M", "--modules", action='store_true', help="List available modules.")
|
|
p.add_argument("-C", "--correlate", metavar="scanID", help="Run correlation rules against a scan ID.")
|
|
p.add_argument("-s", metavar="TARGET", help="Target for the scan.")
|
|
p.add_argument("-t", metavar="type1,type2,...", type=str, help="Event types to collect (modules selected automatically).")
|
|
p.add_argument("-u", choices=["all", "footprint", "investigate", "passive"], type=str, help="Select modules automatically by use case")
|
|
p.add_argument("-T", "--types", action='store_true', help="List available event types.")
|
|
p.add_argument("-o", choices=["tab", "csv", "json"], type=str, help="Output format. Tab is default.")
|
|
p.add_argument("-H", action='store_true', help="Don't print field headers, just data.")
|
|
p.add_argument("-n", action='store_true', help="Strip newlines from data.")
|
|
p.add_argument("-r", action='store_true', help="Include the source data field in tab/csv output.")
|
|
p.add_argument("-S", metavar="LENGTH", type=int, help="Maximum data length to display. By default, all data is shown.")
|
|
p.add_argument("-D", metavar='DELIMITER', type=str, help="Delimiter to use for CSV output. Default is ,.")
|
|
p.add_argument("-f", action='store_true', help="Filter out other event types that weren't requested with -t.")
|
|
p.add_argument("-F", metavar="type1,type2,...", type=str, help="Show only a set of event types, comma-separated.")
|
|
p.add_argument("-x", action='store_true', help="STRICT MODE. Will only enable modules that can directly consume your target, and if -t was specified only those events will be consumed by modules. This overrides -t and -m options.")
|
|
p.add_argument("-q", action='store_true', help="Disable logging. This will also hide errors!")
|
|
p.add_argument("-V", "--version", action='store_true', help="Display the version of SpiderFoot and exit.")
|
|
p.add_argument("-max-threads", type=int, help="Max number of modules to run concurrently.")
|
|
args = p.parse_args()
|
|
|
|
if args.version:
|
|
print(f"SpiderFoot {__version__}: Open Source Intelligence Automation.")
|
|
sys.exit(0)
|
|
|
|
if args.max_threads:
|
|
sfConfig['_maxthreads'] = args.max_threads
|
|
|
|
if args.debug:
|
|
sfConfig['_debug'] = True
|
|
else:
|
|
sfConfig['_debug'] = False
|
|
|
|
if args.q:
|
|
sfConfig['__logging'] = False
|
|
|
|
loggingQueue = mp.Queue()
|
|
logListenerSetup(loggingQueue, sfConfig)
|
|
logWorkerSetup(loggingQueue)
|
|
log = logging.getLogger(f"spiderfoot.{__name__}")
|
|
|
|
# Add descriptions of the global config options
|
|
sfConfig['__globaloptdescs__'] = sfOptdescs
|
|
|
|
# Load each module in the modules directory with a .py extension
|
|
try:
|
|
mod_dir = os.path.dirname(os.path.abspath(__file__)) + '/modules/'
|
|
sfModules = SpiderFootHelpers.loadModulesAsDict(mod_dir, ['sfp_template.py'])
|
|
except BaseException as e:
|
|
log.critical(f"Failed to load modules: {e}", exc_info=True)
|
|
sys.exit(-1)
|
|
|
|
if not sfModules:
|
|
log.critical(f"No modules found in modules directory: {mod_dir}")
|
|
sys.exit(-1)
|
|
|
|
# Load each correlation rule in the correlations directory with
|
|
# a .yaml extension
|
|
try:
|
|
correlations_dir = os.path.dirname(os.path.abspath(__file__)) + '/correlations/'
|
|
correlationRulesRaw = SpiderFootHelpers.loadCorrelationRulesRaw(correlations_dir, ['template.yaml'])
|
|
except BaseException as e:
|
|
log.critical(f"Failed to load correlation rules: {e}", exc_info=True)
|
|
sys.exit(-1)
|
|
|
|
# Initialize database handle
|
|
try:
|
|
dbh = SpiderFootDb(sfConfig)
|
|
except Exception as e:
|
|
log.critical(f"Failed to initialize database: {e}", exc_info=True)
|
|
sys.exit(-1)
|
|
|
|
# Sanity-check the rules and parse them
|
|
sfCorrelationRules = list()
|
|
if not correlationRulesRaw:
|
|
log.error(f"No correlation rules found in correlations directory: {correlations_dir}")
|
|
else:
|
|
try:
|
|
correlator = SpiderFootCorrelator(dbh, correlationRulesRaw)
|
|
sfCorrelationRules = correlator.get_ruleset()
|
|
except Exception as e:
|
|
log.critical(f"Failure initializing correlation rules: {e}", exc_info=True)
|
|
sys.exit(-1)
|
|
|
|
# Add modules and correlation rules to sfConfig so they can be used elsewhere
|
|
sfConfig['__modules__'] = sfModules
|
|
sfConfig['__correlationrules__'] = sfCorrelationRules
|
|
|
|
if args.correlate:
|
|
if not correlationRulesRaw:
|
|
log.error("Unable to perform correlations as no correlation rules were found.")
|
|
sys.exit(-1)
|
|
|
|
try:
|
|
log.info(f"Running {len(correlationRulesRaw)} correlation rules against scan, {args.correlate}.")
|
|
corr = SpiderFootCorrelator(dbh, correlationRulesRaw, args.correlate)
|
|
corr.run_correlations()
|
|
except Exception as e:
|
|
log.critical(f"Unable to run correlation rules: {e}", exc_info=True)
|
|
sys.exit(-1)
|
|
sys.exit(0)
|
|
|
|
if args.modules:
|
|
log.info("Modules available:")
|
|
for m in sorted(sfModules.keys()):
|
|
if "__" in m:
|
|
continue
|
|
print(f"{m.ljust(25)} {sfModules[m]['descr']}")
|
|
sys.exit(0)
|
|
|
|
if args.types:
|
|
dbh = SpiderFootDb(sfConfig, init=True)
|
|
log.info("Types available:")
|
|
typedata = dbh.eventTypes()
|
|
types = dict()
|
|
for r in typedata:
|
|
types[r[1]] = r[0]
|
|
|
|
for t in sorted(types.keys()):
|
|
print(f"{t.ljust(45)} {types[t]}")
|
|
sys.exit(0)
|
|
|
|
if args.l:
|
|
try:
|
|
(host, port) = args.l.split(":")
|
|
except BaseException:
|
|
log.critical("Invalid ip:port format.")
|
|
sys.exit(-1)
|
|
|
|
sfWebUiConfig['host'] = host
|
|
sfWebUiConfig['port'] = port
|
|
|
|
start_web_server(sfWebUiConfig, sfConfig, loggingQueue)
|
|
sys.exit(0)
|
|
|
|
start_scan(sfConfig, sfModules, args, loggingQueue)
|
|
|
|
|
|
def start_scan(sfConfig: dict, sfModules: dict, args, loggingQueue) -> None:
|
|
"""Start scan
|
|
|
|
Args:
|
|
sfConfig (dict): SpiderFoot config options
|
|
sfModules (dict): modules
|
|
args (argparse.Namespace): command line args
|
|
loggingQueue (Queue): main SpiderFoot logging queue
|
|
"""
|
|
log = logging.getLogger(f"spiderfoot.{__name__}")
|
|
|
|
global dbh
|
|
global scanId
|
|
|
|
dbh = SpiderFootDb(sfConfig, init=True)
|
|
sf = SpiderFoot(sfConfig)
|
|
|
|
if not args.s:
|
|
log.error("You must specify a target when running in scan mode. Try --help for guidance.")
|
|
sys.exit(-1)
|
|
|
|
if args.x and not args.t:
|
|
log.error("-x can only be used with -t. Use --help for guidance.")
|
|
sys.exit(-1)
|
|
|
|
if args.x and args.m:
|
|
log.error("-x can only be used with -t and not with -m. Use --help for guidance.")
|
|
sys.exit(-1)
|
|
|
|
if args.r and (args.o and args.o not in ["tab", "csv"]):
|
|
log.error("-r can only be used when your output format is tab or csv.")
|
|
sys.exit(-1)
|
|
|
|
if args.H and (args.o and args.o not in ["tab", "csv"]):
|
|
log.error("-H can only be used when your output format is tab or csv.")
|
|
sys.exit(-1)
|
|
|
|
if args.D and args.o != "csv":
|
|
log.error("-D can only be used when using the csv output format.")
|
|
sys.exit(-1)
|
|
|
|
target = args.s
|
|
# Usernames and names - quoted on the commandline - won't have quotes,
|
|
# so add them.
|
|
if " " in target:
|
|
target = f"\"{target}\""
|
|
if "." not in target and not target.startswith("+") and '"' not in target:
|
|
target = f"\"{target}\""
|
|
targetType = SpiderFootHelpers.targetTypeFromString(target)
|
|
|
|
if not targetType:
|
|
log.error(f"Could not determine target type. Invalid target: {target}")
|
|
sys.exit(-1)
|
|
|
|
target = target.strip('"')
|
|
|
|
modlist = list()
|
|
if not args.t and not args.m and not args.u:
|
|
log.warning("You didn't specify any modules, types or use case, so all modules will be enabled.")
|
|
for m in list(sfModules.keys()):
|
|
if "__" in m:
|
|
continue
|
|
modlist.append(m)
|
|
|
|
signal.signal(signal.SIGINT, handle_abort)
|
|
# If the user is scanning by type..
|
|
# 1. Find modules producing that type
|
|
if args.t:
|
|
types = args.t
|
|
modlist = sf.modulesProducing(types)
|
|
newmods = deepcopy(modlist)
|
|
newmodcpy = deepcopy(newmods)
|
|
|
|
# 2. For each type those modules consume, get modules producing
|
|
while len(newmodcpy) > 0:
|
|
for etype in sf.eventsToModules(newmodcpy):
|
|
xmods = sf.modulesProducing([etype])
|
|
for mod in xmods:
|
|
if mod not in modlist:
|
|
modlist.append(mod)
|
|
newmods.append(mod)
|
|
newmodcpy = deepcopy(newmods)
|
|
newmods = list()
|
|
|
|
# Easier if scanning by module
|
|
if args.m:
|
|
modlist = list(filter(None, args.m.split(",")))
|
|
|
|
# Select modules if the user selected usercase
|
|
if args.u:
|
|
usecase = args.u[0].upper() + args.u[1:] # Make the first Letter Uppercase
|
|
for mod in sfConfig['__modules__']:
|
|
if usecase == 'All' or usecase in sfConfig['__modules__'][mod]['group']:
|
|
modlist.append(mod)
|
|
|
|
# Add sfp__stor_stdout to the module list
|
|
typedata = dbh.eventTypes()
|
|
types = dict()
|
|
for r in typedata:
|
|
types[r[1]] = r[0]
|
|
|
|
sfp__stor_stdout_opts = sfConfig['__modules__']['sfp__stor_stdout']['opts']
|
|
sfp__stor_stdout_opts['_eventtypes'] = types
|
|
if args.f:
|
|
if args.f and not args.t:
|
|
log.error("You can only use -f with -t. Use --help for guidance.")
|
|
sys.exit(-1)
|
|
sfp__stor_stdout_opts['_showonlyrequested'] = True
|
|
if args.F:
|
|
sfp__stor_stdout_opts['_requested'] = args.F.split(",")
|
|
sfp__stor_stdout_opts['_showonlyrequested'] = True
|
|
if args.o:
|
|
if args.o not in ["tab", "csv", "json"]:
|
|
log.error("Invalid output format selected. Must be 'tab', 'csv' or 'json'.")
|
|
sys.exit(-1)
|
|
sfp__stor_stdout_opts['_format'] = args.o
|
|
if args.t:
|
|
sfp__stor_stdout_opts['_requested'] = args.t.split(",")
|
|
if args.n:
|
|
sfp__stor_stdout_opts['_stripnewline'] = True
|
|
if args.r:
|
|
sfp__stor_stdout_opts['_showsource'] = True
|
|
if args.S:
|
|
sfp__stor_stdout_opts['_maxlength'] = args.S
|
|
if args.D:
|
|
sfp__stor_stdout_opts['_csvdelim'] = args.D
|
|
if args.x:
|
|
tmodlist = list()
|
|
modlist = list()
|
|
xmods = sf.modulesConsuming([targetType])
|
|
for mod in xmods:
|
|
if mod not in modlist:
|
|
tmodlist.append(mod)
|
|
|
|
# Remove any modules not producing the type requested
|
|
rtypes = args.t.split(",")
|
|
for mod in tmodlist:
|
|
for r in rtypes:
|
|
if not sfModules[mod]['provides']:
|
|
continue
|
|
if r in sfModules[mod].get('provides', []) and mod not in modlist:
|
|
modlist.append(mod)
|
|
|
|
if len(modlist) == 0:
|
|
log.error("Based on your criteria, no modules were enabled.")
|
|
sys.exit(-1)
|
|
|
|
modlist += ["sfp__stor_db", "sfp__stor_stdout"]
|
|
|
|
if sfConfig['__logging']:
|
|
log.info(f"Modules enabled ({len(modlist)}): {','.join(modlist)}")
|
|
|
|
cfg = sf.configUnserialize(dbh.configGet(), sfConfig)
|
|
|
|
# Debug mode is a variable that gets stored to the DB, so re-apply it
|
|
if args.debug:
|
|
cfg['_debug'] = True
|
|
else:
|
|
cfg['_debug'] = False
|
|
|
|
# If strict mode is enabled, filter the output from modules.
|
|
if args.x and args.t:
|
|
cfg['__outputfilter'] = args.t.split(",")
|
|
|
|
# Prepare scan output headers
|
|
if args.o == "json":
|
|
print("[", end='')
|
|
elif not args.H:
|
|
delim = "\t"
|
|
|
|
if args.o == "tab":
|
|
delim = "\t"
|
|
|
|
if args.o == "csv":
|
|
if args.D:
|
|
delim = args.D
|
|
else:
|
|
delim = ","
|
|
|
|
if args.r:
|
|
if delim == "\t":
|
|
headers = delim.join(["Source".ljust(30), "Type".ljust(45), "Source Data", "Data"])
|
|
else:
|
|
headers = delim.join(["Source", "Type", "Source Data", "Data"])
|
|
else:
|
|
if delim == "\t":
|
|
headers = delim.join(["Source".ljust(30), "Type".ljust(45), "Data"])
|
|
else:
|
|
headers = delim.join(["Source", "Type", "Data"])
|
|
|
|
print(headers)
|
|
|
|
# Start running a new scan
|
|
scanName = target
|
|
scanId = SpiderFootHelpers.genScanInstanceId()
|
|
try:
|
|
p = mp.Process(target=startSpiderFootScanner, args=(loggingQueue, scanName, scanId, target, targetType, modlist, cfg))
|
|
p.daemon = True
|
|
p.start()
|
|
except BaseException as e:
|
|
log.error(f"Scan [{scanId}] failed: {e}")
|
|
sys.exit(-1)
|
|
|
|
# Poll for scan status until completion
|
|
while True:
|
|
time.sleep(1)
|
|
info = dbh.scanInstanceGet(scanId)
|
|
if not info:
|
|
continue
|
|
if info[5] in ["ERROR-FAILED", "ABORT-REQUESTED", "ABORTED", "FINISHED"]:
|
|
# allow 60 seconds for post-scan correlations to complete
|
|
timeout = 60
|
|
p.join(timeout=timeout)
|
|
if (p.is_alive()):
|
|
log.error(f"Timeout reached ({timeout}s) waiting for scan {scanId} post-processing to complete.")
|
|
sys.exit(-1)
|
|
|
|
if sfConfig['__logging']:
|
|
log.info(f"Scan completed with status {info[5]}")
|
|
if args.o == "json":
|
|
print("]")
|
|
sys.exit(0)
|
|
|
|
return
|
|
|
|
|
|
def start_web_server(sfWebUiConfig: dict, sfConfig: dict, loggingQueue=None) -> None:
|
|
"""Start the web server so you can start looking at results
|
|
|
|
Args:
|
|
sfWebUiConfig (dict): web server options
|
|
sfConfig (dict): SpiderFoot config options
|
|
loggingQueue (Queue): main SpiderFoot logging queue
|
|
"""
|
|
log = logging.getLogger(f"spiderfoot.{__name__}")
|
|
|
|
web_host = sfWebUiConfig.get('host', '127.0.0.1')
|
|
web_port = sfWebUiConfig.get('port', 5001)
|
|
web_root = sfWebUiConfig.get('root', '/')
|
|
cors_origins = sfWebUiConfig.get('cors_origins', [])
|
|
|
|
cherrypy.config.update({
|
|
'log.screen': False,
|
|
'server.socket_host': web_host,
|
|
'server.socket_port': int(web_port)
|
|
})
|
|
|
|
log.info(f"Starting web server at {web_host}:{web_port} ...")
|
|
|
|
# Enable access to static files via the web directory
|
|
conf = {
|
|
'/query': {
|
|
'tools.encode.text_only': False,
|
|
'tools.encode.add_charset': True,
|
|
},
|
|
'/static': {
|
|
'tools.staticdir.on': True,
|
|
'tools.staticdir.dir': 'static',
|
|
'tools.staticdir.root': f"{os.path.dirname(os.path.abspath(__file__))}/spiderfoot"
|
|
}
|
|
}
|
|
|
|
secrets = dict()
|
|
passwd_file = SpiderFootHelpers.dataPath() + '/passwd'
|
|
if os.path.isfile(passwd_file):
|
|
if not os.access(passwd_file, os.R_OK):
|
|
log.error("Could not read passwd file. Permission denied.")
|
|
sys.exit(-1)
|
|
|
|
with open(passwd_file, 'r') as f:
|
|
passwd_data = f.readlines()
|
|
|
|
for line in passwd_data:
|
|
if line.strip() == '':
|
|
continue
|
|
|
|
if ':' not in line:
|
|
log.error("Incorrect format of passwd file, must be username:password on each line.")
|
|
sys.exit(-1)
|
|
|
|
u = line.strip().split(":")[0]
|
|
p = ':'.join(line.strip().split(":")[1:])
|
|
|
|
if not u or not p:
|
|
log.error("Incorrect format of passwd file, must be username:password on each line.")
|
|
sys.exit(-1)
|
|
|
|
secrets[u] = p
|
|
|
|
if secrets:
|
|
log.info("Enabling authentication based on supplied passwd file.")
|
|
conf['/'] = {
|
|
'tools.auth_digest.on': True,
|
|
'tools.auth_digest.realm': web_host,
|
|
'tools.auth_digest.get_ha1': auth_digest.get_ha1_dict_plain(secrets),
|
|
'tools.auth_digest.key': random.SystemRandom().randint(0, 99999999)
|
|
}
|
|
else:
|
|
warn_msg = "\n********************************************************************\n"
|
|
warn_msg += "Warning: passwd file contains no passwords. Authentication disabled.\n"
|
|
warn_msg += "Please consider adding authentication to protect this instance!\n"
|
|
warn_msg += "Refer to https://www.spiderfoot.net/documentation/#security.\n"
|
|
warn_msg += "********************************************************************\n"
|
|
log.warning(warn_msg)
|
|
|
|
using_ssl = False
|
|
key_path = SpiderFootHelpers.dataPath() + '/spiderfoot.key'
|
|
crt_path = SpiderFootHelpers.dataPath() + '/spiderfoot.crt'
|
|
if os.path.isfile(key_path) and os.path.isfile(crt_path):
|
|
if not os.access(crt_path, os.R_OK):
|
|
log.critical(f"Could not read {crt_path} file. Permission denied.")
|
|
sys.exit(-1)
|
|
|
|
if not os.access(key_path, os.R_OK):
|
|
log.critical(f"Could not read {key_path} file. Permission denied.")
|
|
sys.exit(-1)
|
|
|
|
log.info("Enabling SSL based on supplied key and certificate file.")
|
|
cherrypy.server.ssl_module = 'builtin'
|
|
cherrypy.server.ssl_certificate = crt_path
|
|
cherrypy.server.ssl_private_key = key_path
|
|
using_ssl = True
|
|
|
|
if using_ssl:
|
|
url = "https://"
|
|
else:
|
|
url = "http://"
|
|
|
|
if web_host == "0.0.0.0": # nosec
|
|
url = f"{url}127.0.0.1:{web_port}"
|
|
else:
|
|
url = f"{url}{web_host}:{web_port}{web_root}"
|
|
cors_origins.append(url)
|
|
|
|
cherrypy_cors.install()
|
|
cherrypy.config.update({
|
|
'cors.expose.on': True,
|
|
'cors.expose.origins': cors_origins,
|
|
'cors.preflight.origins': cors_origins
|
|
})
|
|
|
|
print("")
|
|
print("*************************************************************")
|
|
print(" Use SpiderFoot by starting your web browser of choice and ")
|
|
print(f" browse to {url}")
|
|
print("*************************************************************")
|
|
print("")
|
|
|
|
# Disable auto-reloading of content
|
|
cherrypy.engine.autoreload.unsubscribe()
|
|
|
|
cherrypy.quickstart(SpiderFootWebUi(sfWebUiConfig, sfConfig, loggingQueue), script_name=web_root, config=conf)
|
|
|
|
|
|
def handle_abort(signal, frame) -> None:
|
|
"""Handle interrupt and abort scan.
|
|
|
|
Args:
|
|
signal: TBD
|
|
frame: TBD
|
|
"""
|
|
log = logging.getLogger(f"spiderfoot.{__name__}")
|
|
|
|
global dbh
|
|
global scanId
|
|
|
|
if scanId and dbh:
|
|
log.info(f"Aborting scan [{scanId}] ...")
|
|
dbh.scanInstanceSet(scanId, None, None, "ABORTED")
|
|
sys.exit(-1)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
if sys.version_info < (3, 7):
|
|
print("SpiderFoot requires Python 3.7 or higher.")
|
|
sys.exit(-1)
|
|
|
|
if len(sys.argv) <= 1:
|
|
print("SpiderFoot requires -l <ip>:<port> to start the web server. Try --help for guidance.")
|
|
sys.exit(-1)
|
|
|
|
# TODO: remove this after a few releases (added in 3.5 pre-release 2021-09-05)
|
|
from pathlib import Path
|
|
if os.path.exists('spiderfoot.db'):
|
|
print(f"ERROR: spiderfoot.db file exists in {os.path.dirname(__file__)}")
|
|
print("SpiderFoot no longer supports loading the spiderfoot.db database from the application directory.")
|
|
print(f"The database is now loaded from your home directory: {Path.home()}/.spiderfoot/spiderfoot.db")
|
|
print(f"This message will go away once you move or remove spiderfoot.db from {os.path.dirname(__file__)}")
|
|
sys.exit(-1)
|
|
|
|
# TODO: remove this after a few releases (added in 3.5 pre-release 2021-09-05)
|
|
from pathlib import Path
|
|
if os.path.exists('passwd'):
|
|
print(f"ERROR: passwd file exists in {os.path.dirname(__file__)}")
|
|
print("SpiderFoot no longer supports loading credentials from the application directory.")
|
|
print(f"The passwd file is now loaded from your home directory: {Path.home()}/.spiderfoot/passwd")
|
|
print(f"This message will go away once you move or remove passwd from {os.path.dirname(__file__)}")
|
|
sys.exit(-1)
|
|
|
|
main()
|