def create(self): # if options().domain: # domain_regex = r"[a-zA-Z0-9].*" # domain = re.findall(domain_regex, options().domain)[0] #print(domain) # Specify regex to be used # Regex should grab every sub domain after domain regex = f"\w*\.(?={options().domain})" # Create a set to remove duplicates subset = set() with open(options().file, "r") as f: try: for domains in f: # Remove new line character domains = domains.strip("\n") domains = re.findall(regex, domains)[0] # Remove "." after sub domain output subset.add(domains.strip(".")) # If there is an index error, pass and keep parsing except IndexError: pass print("\n".join(subset)) # If write to outfile is specified if options().out: with open(options().out, "a") as wf: wf.write("\n".join(subset)) wf.write("\n") print(f"\nYour results have been written too {options().out}")
def print_domains(self): subdomains = "\n".join(self.domains) print(f"{self.BLUE}{subdomains}{self.RESET}") if options().out: with open(options().out, "a") as f: f.write(str(subdomains)) f.write("\n")
def securitytrails(self): try: if options().api: with open(self.config_file, "r") as f: jfile = json.load(f) self.apikey = jfile["API_INFO"][4]["key"] if self.apikey == "": print( f"{self.YELLOW}[!] API credentials not found for {self.WHITE}securitytrails.{self.RESET}" ) api_url = f"https://api.securitytrails.com/v1/domain/{self.domain}/subdomains" params = {"apikey": f"{self.apikey}"} response = requests.get( api_url, params=params, headers={ 'User-Agent': subseeker_core.useragents.useragent() }) data = json.loads(response.text) if data: for row in data["subdomains"]: row = f"{row}.{self.domain}" self.domains.add(row) elif not data: print( f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}securitytrails.{self.RESET}" ) elif not options().api: if options().verbose: print( f"{self.YELLOW}[!] API credentials needed for {self.WHITE}securitytrails.{self.RESET}" ) pass elif not options().api: pass except KeyError as e: pass except IndexError as e: pass except ValueError as e: pass except AttributeError: print(f"{self.YELLOW}[!] Config file not found!{self.RESET}") print( f"{self.YELLOW}[!] System exiting now. Fix config file or run without --api command.{self.RESET}\n" ) sys.exit(1)
def title(self): if options().domain: domain_regex = r"[a-zA-Z0-9].*" domain = re.findall(domain_regex, options().domain)[0] print(f"{self.RED}\t\tSUBSEEKER{self.RESET}") print(f"{self.RED}\t========================={self.RESET}") print(f"{self.BLUE}\t DOMAIN:{self.RESET} {domain}") # Display choosen header, if not chosen, display default if options().useragent: print( f"{self.BLUE}\t User-Agent:{self.RESET} {options().useragent}" ) elif not options().useragent: print(f"{self.BLUE}\t User-Agent:{self.RESET} Firefox") # If user uses a file or keywords option, count number of subdomain keywords being used if options().file: print(f"{self.BLUE}\t File:{self.RESET} {options().file}") count_lines = sum(1 for line in open(options().file)) print(f"{self.BLUE}\t Keywords:{self.RESET} {count_lines}") elif options().keywords: print( f"{self.BLUE}\t Keywords:{self.RESET} {len(options().keywords)}" ) if options().threads: print(f"{self.BLUE}\t Threads:{self.RESET} {options().threads}") elif not options().threads: print(f"{self.BLUE}\t Threads:{self.RESET} 20") print("\n")
def certdb(self): if options().page: page = options().page else: page = self.page try: # Default false if options().api: with open(self.config_file, "r") as f: jfile = json.load(f) self.apikey = jfile["API_INFO"][1]["key"] if self.apikey == "": print( f"{self.YELLOW}[!] API credentials not found for {self.WHITE}certdb.{self.RESET}" ) url = f"https://api.spyse.com/v1/subdomains?api_token={self.apikey}&domain={self.domain}&page={page}" response = requests.get( url, headers={'User-Agent': subseeker_core.useragents.useragent()}) data = response.json() if data: for row in data["records"]: subdomains = row["domain"] self.domains.add(subdomains) elif not data: print( f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}certdb. {self.YELLOW}Page: {page}{self.RESET}" ) except KeyError: pass except IndexError: pass except ValueError: pass except AttributeError: print(f"{self.YELLOW}[!] Config file not found!{self.RESET}") print( f"{self.YELLOW}[!] System exiting now. Fix config file or run without --api command.{self.RESET}\n" ) sys.exit(1)
def version(self): if options().version: print(f"{self.YELLOW}\t Title:{self.RESET} Subseeker") print(f"{self.YELLOW}\t Version:{self.RESET} 2.1.2") print(f"{self.YELLOW}\t Author:{self.RESET} Matthew Greer") print( f"{self.YELLOW}\t Twitter:{self.RESET} https://twitter.com/Vail__" ) print( f"{self.YELLOW}\t Github:{self.RESET} https://github.com/DFC302" )
def certspotter(self): try: if options().api: with open(self.config_file, "r") as f: jfile = json.load(f) self.apikey = jfile["API_INFO"][0]["key"] if self.apikey == "": print( f"{self.YELLOW}[!] API credentials not found for {self.WHITE}certspotter.{self.RESET}" ) else: params = {'Authorization': 'Bearer ' + self.apikey} url = f"https://api.certspotter.com/v1/issuances?domain={self.domain}&include_subdomains=true&expand=dns_names&expand=cert" response = requests.get( url, params=self.params, headers={'User-Agent': subseeker_core.useragents.useragent()}) data = response.json() if data: for row in data: row = row["dns_names"] for domain in row: self.domains.add(row) elif not data: print( f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}certspotter.{self.RESET}" ) except TypeError as e: pass except IndexError: pass except KeyError: pass except ValueError: pass except AttributeError: print(f"{self.YELLOW}[!] Config file not found!{self.RESET}") print( f"{self.YELLOW}[!] System exiting now. Fix config file or run without --api command.{self.RESET}\n" ) sys.exit(1)
def thread_execution(self): domains = [] if options().file: with open(options().file, "r") as f: for sub in f: if sub == "": pass else: sub = str(sub.strip("\n")) domains.append(sub) elif options().keywords: for sub in options().keywords: domains.append(sub) if options().threads: threads = options().threads else: threads = self.threads with concurrent.futures.ThreadPoolExecutor( max_workers=threads) as executor: executor.map(self.multi_keyword_search, domains)
def useragent(): # Chrome header if options().useragent == "chrome".lower(): ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \ (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36" return ua # Firefox header, default elif options().useragent == "firefox".lower(): ua = "Mozilla/5.0 (Windows NT 5.1; rv:7.0.1) Gecko/20100101 \ Firefox/7.0.1" return ua # Opera header elif options().useragent == "opera".lower(): ua = "Opera/9.80 (Windows NT 6.1; WOW64) Presto/2.12.388 Version/12.18" return ua # If no header specifed, use set firefox elif not options().useragent: ua = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 \ Firefox/40.1" return ua
def crtsh(self): org_domain = options().domain.replace("*", "%25") url = f"https://crt.sh/?q={org_domain}&output=json" try: response = requests.get( url, headers={'User-Agent': subseeker_core.useragents.useragent()}) regex = r'[^%*].*' data = response.json() if data: for row in data: row = re.findall(regex, row["name_value"])[0] self.domains.add(row) elif not data: print( f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}crtsh.{self.RESET}" ) except ValueError: pass
def virustotal(self): try: if options().api: with open(self.config_file, "r") as f: jfile = json.load(f) self.apikey = jfile["API_INFO"][3]["key"] if self.apikey == "": print( f"{self.YELLOW}[!] API credentials not found for {self.WHITE}virustotal.{self.RESET}" ) api_url = "https://www.virustotal.com/vtapi/v2/domain/report" params = { "apikey": f"{self.apikey}", "domain": f"{self.domain}" } response = requests.get( api_url, params=params, headers={ 'User-Agent': subseeker_core.useragents.useragent() }) data = response.json() if data: subdomains = "\n".join(data["subdomains"]) self.domains.add(subdomains) elif not data: print( f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}virustotal.{self.RESET}" ) elif not options().api: if options().verbose: print( f"{self.YELLOW}[!] API credentails needed for {self.WHITE}virustotal.{self.RESET}" ) pass elif not options().api: pass except KeyError: pass except IndexError: pass except ValueError: pass except AttributeError: print(f"{self.YELLOW}[!] Config file not found!{self.RESET}") print( f"{self.YELLOW}[!] System exiting now. Fix config file or run without --api command.{self.RESET}\n" ) sys.exit(1)
def censys(self): if options().page: page = options().page else: page = self.page try: if options().api: with open(self.config_file, "r") as f: jfile = json.load(f) self.apikey = jfile["API_INFO"][2]["id"] secret = jfile["API_INFO"][2]["secret"] if self.apikey == "" or secret == "": print( f"{self.YELLOW}[!] API credentials not found for {self.WHITE}censys.{self.RESET}" ) api_url = "https://censys.io/api/v1/search/certificates" regex = r'(?:CN=).+' params = {"query": f"{self.domain}", "page": page} response = requests.post( api_url, json=params, auth=(self.apikey, secret), headers={ 'User-Agent': subseeker_core.useragents.useragent() }) data = response.json() if data: for row in data["results"]: CN = row["parsed.subject_dn"].splitlines() for line in CN: line = re.findall(regex, line)[0][3:] self.domains.add(line) elif not data: print( f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}censys. {self.YELLOW}Page: {page}{self.RESET}" ) elif not options().api: if options().verbose: print( f"{self.YELLOW}[!] API credentials needed for {self.WHITE}censys.{self.RESET}" ) pass elif not options().api: pass except IndexError: pass except KeyError: pass except ValueError: pass except AttributeError: print(f"{self.YELLOW}[!] Config file not found!{self.RESET}") print( f"{self.YELLOW}[!] System exiting now. Fix config file or run without --api command.{self.RESET}\n" ) sys.exit(1)
class SubSeeker(): GREEN = Fore.GREEN RED = Fore.RED YELLOW = Fore.YELLOW BLUE = Fore.BLUE WHITE = Fore.WHITE RESET = Style.RESET_ALL if options().domain: # domain_regex = r"([^\.]*.(?=com).+)" domain_regex = r"[a-zA-Z0-9].*" domain = re.findall(domain_regex, options().domain)[0] # if user uses api, search for config file, that way no matter what directory user is in, # subseeker can find config file. if options().api: if options().verbose: print( f"\n{YELLOW}[!] Looking for config file containing API credentials...{RESET}" ) for root, dirs, files in os.walk("/"): filename = "subseeker_config.json" if filename in files: config_file = os.path.join(root, filename) if options().verbose: print( f"{GREEN}[+] Config file found: {config_file}{RESET}") print(f"{GREEN}[+] Starting search...{RESET}\n") domains = set() # Default value for API key is false, if user does not have one. def __init__(self, apikey=False, params=None, page=1): self.apikey = apikey self.params = params self.page = page self.threads = 20 def crtsh(self): org_domain = options().domain.replace("*", "%25") url = f"https://crt.sh/?q={org_domain}&output=json" try: response = requests.get( url, headers={'User-Agent': subseeker_core.useragents.useragent()}) regex = r'[^%*].*' data = response.json() if data: for row in data: row = re.findall(regex, row["name_value"])[0] self.domains.add(row) elif not data: print( f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}crtsh.{self.RESET}" ) except ValueError: pass def certspotter(self): try: if options().api: with open(self.config_file, "r") as f: jfile = json.load(f) self.apikey = jfile["API_INFO"][0]["key"] if self.apikey == "": print( f"{self.YELLOW}[!] API credentials not found for {self.WHITE}certspotter.{self.RESET}" ) else: params = {'Authorization': 'Bearer ' + self.apikey} url = f"https://api.certspotter.com/v1/issuances?domain={self.domain}&include_subdomains=true&expand=dns_names&expand=cert" response = requests.get( url, params=self.params, headers={'User-Agent': subseeker_core.useragents.useragent()}) data = response.json() if data: for row in data: row = row["dns_names"] for domain in row: self.domains.add(row) elif not data: print( f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}certspotter.{self.RESET}" ) except TypeError as e: pass except IndexError: pass except KeyError: pass except ValueError: pass except AttributeError: print(f"{self.YELLOW}[!] Config file not found!{self.RESET}") print( f"{self.YELLOW}[!] System exiting now. Fix config file or run without --api command.{self.RESET}\n" ) sys.exit(1) def certdb(self): if options().page: page = options().page else: page = self.page try: # Default false if options().api: with open(self.config_file, "r") as f: jfile = json.load(f) self.apikey = jfile["API_INFO"][1]["key"] if self.apikey == "": print( f"{self.YELLOW}[!] API credentials not found for {self.WHITE}certdb.{self.RESET}" ) url = f"https://api.spyse.com/v1/subdomains?api_token={self.apikey}&domain={self.domain}&page={page}" response = requests.get( url, headers={'User-Agent': subseeker_core.useragents.useragent()}) data = response.json() if data: for row in data["records"]: subdomains = row["domain"] self.domains.add(subdomains) elif not data: print( f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}certdb. {self.YELLOW}Page: {page}{self.RESET}" ) except KeyError: pass except IndexError: pass except ValueError: pass except AttributeError: print(f"{self.YELLOW}[!] Config file not found!{self.RESET}") print( f"{self.YELLOW}[!] System exiting now. Fix config file or run without --api command.{self.RESET}\n" ) sys.exit(1) def censys(self): if options().page: page = options().page else: page = self.page try: if options().api: with open(self.config_file, "r") as f: jfile = json.load(f) self.apikey = jfile["API_INFO"][2]["id"] secret = jfile["API_INFO"][2]["secret"] if self.apikey == "" or secret == "": print( f"{self.YELLOW}[!] API credentials not found for {self.WHITE}censys.{self.RESET}" ) api_url = "https://censys.io/api/v1/search/certificates" regex = r'(?:CN=).+' params = {"query": f"{self.domain}", "page": page} response = requests.post( api_url, json=params, auth=(self.apikey, secret), headers={ 'User-Agent': subseeker_core.useragents.useragent() }) data = response.json() if data: for row in data["results"]: CN = row["parsed.subject_dn"].splitlines() for line in CN: line = re.findall(regex, line)[0][3:] self.domains.add(line) elif not data: print( f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}censys. {self.YELLOW}Page: {page}{self.RESET}" ) elif not options().api: if options().verbose: print( f"{self.YELLOW}[!] API credentials needed for {self.WHITE}censys.{self.RESET}" ) pass elif not options().api: pass except IndexError: pass except KeyError: pass except ValueError: pass except AttributeError: print(f"{self.YELLOW}[!] Config file not found!{self.RESET}") print( f"{self.YELLOW}[!] System exiting now. Fix config file or run without --api command.{self.RESET}\n" ) sys.exit(1) def virustotal(self): try: if options().api: with open(self.config_file, "r") as f: jfile = json.load(f) self.apikey = jfile["API_INFO"][3]["key"] if self.apikey == "": print( f"{self.YELLOW}[!] API credentials not found for {self.WHITE}virustotal.{self.RESET}" ) api_url = "https://www.virustotal.com/vtapi/v2/domain/report" params = { "apikey": f"{self.apikey}", "domain": f"{self.domain}" } response = requests.get( api_url, params=params, headers={ 'User-Agent': subseeker_core.useragents.useragent() }) data = response.json() if data: subdomains = "\n".join(data["subdomains"]) self.domains.add(subdomains) elif not data: print( f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}virustotal.{self.RESET}" ) elif not options().api: if options().verbose: print( f"{self.YELLOW}[!] API credentails needed for {self.WHITE}virustotal.{self.RESET}" ) pass elif not options().api: pass except KeyError: pass except IndexError: pass except ValueError: pass except AttributeError: print(f"{self.YELLOW}[!] Config file not found!{self.RESET}") print( f"{self.YELLOW}[!] System exiting now. Fix config file or run without --api command.{self.RESET}\n" ) sys.exit(1) def threatcrowd(self): try: api_url = "http://www.threatcrowd.org/searchApi/v2/domain/report/" params = {"domain": f"{self.domain}"} response = requests.get( api_url, params=params, headers={'User-Agent': subseeker_core.useragents.useragent()}) data = json.loads(response.text) data = data["subdomains"] if data: for row in data: self.domains.add(row) elif not data: print( f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}threatcrowd.{self.RESET}" ) except KeyError: pass except IndexError: pass except ValueError: pass def securitytrails(self): try: if options().api: with open(self.config_file, "r") as f: jfile = json.load(f) self.apikey = jfile["API_INFO"][4]["key"] if self.apikey == "": print( f"{self.YELLOW}[!] API credentials not found for {self.WHITE}securitytrails.{self.RESET}" ) api_url = f"https://api.securitytrails.com/v1/domain/{self.domain}/subdomains" params = {"apikey": f"{self.apikey}"} response = requests.get( api_url, params=params, headers={ 'User-Agent': subseeker_core.useragents.useragent() }) data = json.loads(response.text) if data: for row in data["subdomains"]: row = f"{row}.{self.domain}" self.domains.add(row) elif not data: print( f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}securitytrails.{self.RESET}" ) elif not options().api: if options().verbose: print( f"{self.YELLOW}[!] API credentials needed for {self.WHITE}securitytrails.{self.RESET}" ) pass elif not options().api: pass except KeyError as e: pass except IndexError as e: pass except ValueError as e: pass except AttributeError: print(f"{self.YELLOW}[!] Config file not found!{self.RESET}") print( f"{self.YELLOW}[!] System exiting now. Fix config file or run without --api command.{self.RESET}\n" ) sys.exit(1) def thread_execution(self): domains = [] if options().file: with open(options().file, "r") as f: for sub in f: if sub == "": pass else: sub = str(sub.strip("\n")) domains.append(sub) elif options().keywords: for sub in options().keywords: domains.append(sub) if options().threads: threads = options().threads else: threads = self.threads with concurrent.futures.ThreadPoolExecutor( max_workers=threads) as executor: executor.map(self.multi_keyword_search, domains) # Parse crt.sh for each sub domain keyword def multi_keyword_search(self, sub): try: url = f"https://crt.sh/?q=%25{sub}%25.{self.domain}&output=json" response = requests.get( url, headers={'User-Agent': subseeker_core.useragents.useragent()}) regex = r'[^%*].*' data = response.json() if data: for row in data: row = re.findall(regex, row["name_value"])[0] self.domains.add(row) elif not data: print( f"{self.RED}[x] No data found:{self.WHITE} {sub}{self.RESET}" ) # JSON Decode Error except ValueError: print( f"{self.YELLOW}[!]{self.RED} JSON value error:{self.WHITE} {sub}{self.RESET}" ) pass def print_domains(self): subdomains = "\n".join(self.domains) print(f"{self.BLUE}{subdomains}{self.RESET}") if options().out: with open(options().out, "a") as f: f.write(str(subdomains)) f.write("\n")
def main(): GREEN = Fore.GREEN CYAN = Fore.CYAN MAG = Fore.MAGENTA RED = Fore.RED YELLOW = Fore.YELLOW BLUE = Fore.BLUE RESET = Style.RESET_ALL if options().version: Process().version() sys.exit(0) elif options().generate: if not options().domain: print(f"\n{RED}Error: Need domain to parse against!{RESET}\n") sys.exit(1) elif not options().file: print( f"\n{RED}Error: Need file to generate keywords from!{RESET}\n") sys.exit(1) elif options().file and options().domain: GenerateKeywords().create() sys.exit(0) elif options().file or options().keywords and not options().singlesearch: if not options().domain: print(f"\n{RED}Error: Missing domain.{RESET}\n") sys.exit(1) try: Process().title() Process().process() SubSeeker().thread_execution() SubSeeker().certspotter() SubSeeker().certdb() SubSeeker().threatcrowd() SubSeeker().censys() SubSeeker().virustotal() SubSeeker().securitytrails() SubSeeker().print_domains() sys.exit(0) except KeyboardInterrupt: sys.exit(0) elif options().singlesearch: if options().singlesearch.lower() == "options": print("\tcrtsh\t\tSearch crtsh database.") print("\tmcrtsh\t\tSearch crtsh database using keywords.") print("\tcertspotter\tSearch certspotter database.") print("\tcertdb\t\tSearch certdb database.") print("\tcensys\t\tSearch censys database.") print("\tvirustotal\tSearch virustotal database.") print("\tthreatcrowd\tSearch threatcrowd database.") print("\tsecuritytrails\tSearch securitytrails database.") if not options().domain and not options().singlesearch == "options": print(f"\n{RED}Error: Missing domain.{RESET}\n") sys.exit(1) if options().singlesearch.lower() == "crtsh": Process().title() print(f"{MAG}[~]{CYAN} Checking Crtsh...{RESET}\n") SubSeeker().crtsh() SubSeeker().print_domains() sys.exit(0) elif options().singlesearch.lower() == "certspotter": Process().title() print(f"{MAG}[~]{CYAN} Checking Certspotter...{RESET}\n") SubSeeker().certspotter() SubSeeker().print_domains() sys.exit(0) elif options().singlesearch.lower() == "certdb": Process().title() print(f"{MAG}[~]{CYAN} Checking CertDB...{RESET}\n") SubSeeker().certdb() SubSeeker().print_domains() sys.exit(0) elif options().singlesearch.lower() == "threatcrowd": Process().title() print(f"{MAG}[~]{CYAN} Checking threatcrowd...{RESET}\n") SubSeeker().threatcrowd() SubSeeker().print_domains() sys.exit(0) elif options().singlesearch.lower() == "censys": Process().title() print(f"{MAG}[~]{CYAN} Checking Censys...{RESET}\n") SubSeeker().censys() SubSeeker().print_domains() sys.exit(0) elif options().singlesearch.lower() == "virustotal": Process().title() print(f"{MAG}[~]{CYAN} Checking VirusTotal...{RESET}\n") SubSeeker().virustotal() SubSeeker().print_domains() sys.exit(0) elif options().singlesearch.lower() == "securitytrails": Process().title() print(f"{MAG}[~]{CYAN} Checking SecurityTrails...{RESET}\n") SubSeeker().securitytrails() SubSeeker().print_domains() sys.exit(0) elif options().singlesearch.lower() == "mcrtsh": if options().file or options().keywords and options().domain: Process().title() print(f"{MAG}[~]{CYAN} Checking Crtsh...{RESET}\n") SubSeeker().thread_execution() SubSeeker().print_domains() sys.exit(0) elif not options().file or not options().keywords: print( f"\n{RED}Error! Need --file with list of keywords or at least one --keyword.\n" ) sys.exit(1) else: if not options().domain: print(f"\n{RED}Error: Missing domain.{RESET}\n") sys.exit(1) try: Process().title() Process().process() SubSeeker().crtsh() SubSeeker().certspotter() SubSeeker().certdb() SubSeeker().threatcrowd() SubSeeker().censys() SubSeeker().virustotal() SubSeeker().securitytrails() SubSeeker().print_domains() sys.exit(0) except KeyboardInterrupt: sys.exit(0)