├── subseeker_core ├── __init__.py ├── subseeker_config.json ├── useragents.py ├── create.py ├── configurations.py ├── options.py ├── subseeker.py └── searchmodes.py ├── images └── logo.jpg ├── wordlists └── keywords.txt ├── LICENSE ├── setup.py ├── CHANGELOG.md └── README.md /subseeker_core/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /images/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DFC302/subseeker/HEAD/images/logo.jpg -------------------------------------------------------------------------------- /wordlists/keywords.txt: -------------------------------------------------------------------------------- 1 | mail 2 | mx 3 | ns 4 | remote 5 | smtp 6 | blog 7 | server 8 | redbus 9 | vpn 10 | dev 11 | secure 12 | shop 13 | cloud 14 | ftp 15 | api 16 | portal 17 | test 18 | dns 19 | email 20 | host 21 | app 22 | support 23 | pop 24 | bbs 25 | web 26 | forum 27 | owa 28 | biz 29 | ops 30 | jenkins 31 | -------------------------------------------------------------------------------- /subseeker_core/subseeker_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "API_INFO":[ 3 | { 4 | "certspotter": "01", 5 | "key": "" 6 | }, 7 | 8 | { 9 | "certdb": "02", 10 | "key": "" 11 | }, 12 | 13 | { 14 | "censys": "03", 15 | "id": "", 16 | "secret": "" 17 | }, 18 | 19 | { 20 | "virustotal": "04", 21 | "key": "" 22 | }, 23 | 24 | { 25 | "securitytrails": "05", 26 | "key": "" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /subseeker_core/useragents.py: -------------------------------------------------------------------------------- 1 | from subseeker_core.options import options 2 | 3 | def useragent(): 4 | # Chrome header 5 | if options().useragent == "chrome".lower(): 6 | ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \ 7 | (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36" 8 | return ua 9 | # Firefox header, default 10 | elif options().useragent == "firefox".lower(): 11 | ua = "Mozilla/5.0 (Windows NT 5.1; rv:7.0.1) Gecko/20100101 \ 12 | Firefox/7.0.1" 13 | return ua 14 | # Opera header 15 | elif options().useragent == "opera".lower(): 16 | ua = "Opera/9.80 (Windows NT 6.1; WOW64) Presto/2.12.388 Version/12.18" 17 | return ua 18 | # If no header specifed, use set firefox 19 | elif not options().useragent: 20 | ua = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 \ 21 | Firefox/40.1" 22 | return ua 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2010-2019 Google, Inc. http://angularjs.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r") as f: 4 | long_description = f.read() 5 | 6 | setuptools.setup( 7 | name='subseeker', 8 | version='2.1.2', 9 | author="Matthew Greer", 10 | author_email="pydev302@gmail.com", 11 | license='MIT', 12 | description="A sub enumeration tool.", 13 | long_description=long_description, 14 | long_description_content_type="text/markdown", 15 | url="https://github.com/DFC302/subseeker", 16 | keywords=['enumeration', 'domain', 'sub', 'tool'], 17 | packages=setuptools.find_packages(), 18 | install_requires=[ 19 | "requests", 20 | "argparse", 21 | "termcolor", 22 | ], 23 | package_data={'': ['LICENSE'], '': ['README.md'], '': ['wordlists.keywords.txt'], '': ['core.subseeker_config.json'],}, 24 | include_package_data=True, 25 | classifiers=[ 26 | "Programming Language :: Python :: 3", 27 | "License :: OSI Approved :: MIT License", 28 | "Operating System :: OS Independent", 29 | ], 30 | entry_points={ 31 | 'console_scripts': [ 32 | "subseeker = subseeker_core.subseeker:main", 33 | ], 34 | }, 35 | ) 36 | -------------------------------------------------------------------------------- /subseeker_core/create.py: -------------------------------------------------------------------------------- 1 | from subseeker_core.options import options 2 | import re 3 | 4 | class GenerateKeywords(): 5 | # Create sub domain keywords to search from 6 | def create(self): 7 | # if options().domain: 8 | # domain_regex = r"[a-zA-Z0-9].*" 9 | # domain = re.findall(domain_regex, options().domain)[0] 10 | 11 | #print(domain) 12 | # Specify regex to be used 13 | # Regex should grab every sub domain after domain 14 | regex = f"\w*\.(?={options().domain})" 15 | 16 | # Create a set to remove duplicates 17 | subset=set() 18 | 19 | with open(options().file, "r") as f: 20 | try: 21 | for domains in f: 22 | # Remove new line character 23 | domains = domains.strip("\n") 24 | domains = re.findall(regex, domains)[0] 25 | # Remove "." after sub domain output 26 | subset.add(domains.strip(".")) 27 | 28 | # If there is an index error, pass and keep parsing 29 | except IndexError: 30 | pass 31 | 32 | print("\n".join(subset)) 33 | 34 | # If write to outfile is specified 35 | if options().out: 36 | with open(options().out, "a") as wf: 37 | wf.write("\n".join(subset)) 38 | wf.write("\n") 39 | print(f"\nYour results have been written too {options().out}") 40 | -------------------------------------------------------------------------------- /subseeker_core/configurations.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from colorama import Fore, Style 4 | from subseeker_core.options import options 5 | import os 6 | import re 7 | 8 | class Process(): 9 | GREEN = Fore.GREEN 10 | CYAN = Fore.CYAN 11 | MAG = Fore.MAGENTA 12 | RED = Fore.RED 13 | YELLOW = Fore.YELLOW 14 | BLUE = Fore.BLUE 15 | RESET = Style.RESET_ALL 16 | 17 | 18 | def process(self): 19 | print(f"{self.MAG}[~]{self.CYAN} Checking Crtsh...{self.RESET}") 20 | print(f"{self.MAG}[~]{self.CYAN} Checking Certspotter...{self.RESET}") 21 | print(f"{self.MAG}[~]{self.CYAN} Checking CertDB...{self.RESET}") 22 | print(f"{self.MAG}[~]{self.CYAN} Checking Censys...{self.RESET}") 23 | print(f"{self.MAG}[~]{self.CYAN} Checking VirusTotal...{self.RESET}") 24 | print(f"{self.MAG}[~]{self.CYAN} Checking ThreatCrowd...{self.RESET}") 25 | print(f"{self.MAG}[~]{self.CYAN} Checking SecurityTrails...{self.RESET}") 26 | print("\n") 27 | 28 | def title(self): 29 | if options().domain: 30 | domain_regex = r"[a-zA-Z0-9].*" 31 | domain = re.findall(domain_regex, options().domain)[0] 32 | 33 | print(f"{self.RED}\t\tSUBSEEKER{self.RESET}") 34 | print(f"{self.RED}\t========================={self.RESET}") 35 | print(f"{self.BLUE}\t DOMAIN:{self.RESET} {domain}") 36 | 37 | # Display choosen header, if not chosen, display default 38 | if options().useragent: 39 | print(f"{self.BLUE}\t User-Agent:{self.RESET} {options().useragent}") 40 | elif not options().useragent: 41 | print(f"{self.BLUE}\t User-Agent:{self.RESET} Firefox") 42 | 43 | # If user uses a file or keywords option, count number of subdomain keywords being used 44 | if options().file: 45 | print(f"{self.BLUE}\t File:{self.RESET} {options().file}") 46 | count_lines = sum(1 for line in open(options().file)) 47 | print(f"{self.BLUE}\t Keywords:{self.RESET} {count_lines}") 48 | 49 | elif options().keywords: 50 | print(f"{self.BLUE}\t Keywords:{self.RESET} {len(options().keywords)}") 51 | 52 | if options().threads: 53 | print(f"{self.BLUE}\t Threads:{self.RESET} {options().threads}") 54 | elif not options().threads: 55 | print(f"{self.BLUE}\t Threads:{self.RESET} 20") 56 | 57 | print("\n") 58 | 59 | def version(self): 60 | if options().version: 61 | print(f"{self.YELLOW}\t Title:{self.RESET} Subseeker") 62 | print(f"{self.YELLOW}\t Version:{self.RESET} 2.1.2") 63 | print(f"{self.YELLOW}\t Author:{self.RESET} Matthew Greer") 64 | print(f"{self.YELLOW}\t Twitter:{self.RESET} https://twitter.com/Vail__") 65 | print(f"{self.YELLOW}\t Github:{self.RESET} https://github.com/DFC302") 66 | 67 | def path_to_config(): 68 | name = "subseeker_config.json" 69 | path = "/" 70 | for root, dirs, files in os.walk(path): 71 | if name in files: 72 | filename = os.path.join(root, name) 73 | return filename 74 | -------------------------------------------------------------------------------- /subseeker_core/options.py: -------------------------------------------------------------------------------- 1 | # This file contains information regarding command line arguments, title 2 | # information and version information. 3 | 4 | import argparse 5 | import sys 6 | from termcolor import colored 7 | 8 | # User options 9 | def options(): 10 | parser = argparse.ArgumentParser() 11 | 12 | # specify domain 13 | parser.add_argument( 14 | "--domain", 15 | help="Specify domain to search.", 16 | action="store", 17 | ) 18 | 19 | # single search mode 20 | parser.add_argument( 21 | "--singlesearch", 22 | help="Search using a specific certificate site. Use --singlesearch options to list available search options.", 23 | action="store", 24 | type=str, 25 | ) 26 | 27 | # User can specify keywords instead of a file full of sub keywords 28 | parser.add_argument( 29 | "--keywords", 30 | nargs="+", 31 | help="Add a list of keywords.", 32 | type=str, 33 | ) 34 | 35 | # Parse subdomain keywords from other tools output files 36 | parser.add_argument( 37 | "--generate", 38 | help="Create a list of sub domain keywords from a file containing \ 39 | subdomains.", 40 | action="store_true", 41 | ) 42 | 43 | # search domain using subdomain keywords from file 44 | parser.add_argument( 45 | "--file", 46 | help="Specify a file containing keywords to parse crt.sh OR to create \ 47 | sub keywords from.", 48 | action="store", 49 | ) 50 | 51 | # Write to output file 52 | parser.add_argument( 53 | "--out", 54 | help="Specify a file to write results too.", 55 | action="store", 56 | ) 57 | 58 | # User specify number of threads 59 | parser.add_argument( 60 | "--threads", 61 | help="Specify number of threads to be used when performing keyword \ 62 | search.", 63 | action="store", 64 | type=int, 65 | ) 66 | 67 | # Try with different headers, firefox, chrome, opera 68 | parser.add_argument( 69 | "--useragent", 70 | help="Specify a user-agent to use. Default is a firefox UA.", 71 | action="store", 72 | type=str 73 | ) 74 | 75 | # If API information has been configured, allow use of API credentials 76 | parser.add_argument( 77 | "--api", 78 | help="Turn on api.", 79 | action="store_true", 80 | ) 81 | 82 | # Specify page number for certdb and/or censys 83 | parser.add_argument( 84 | "--page", 85 | help="Used with certdb and/or censys searchmodes. Specify page number to display.", 86 | action="store", 87 | type=int, 88 | ) 89 | 90 | parser.add_argument( 91 | "--version", 92 | help="Display version information", 93 | action="store_true", 94 | ) 95 | 96 | parser.add_argument( 97 | "--verbose", 98 | help="Display extra verbose information, such as errors.", 99 | action="store_true", 100 | ) 101 | 102 | # if not arguments are given, print usage message 103 | if len(sys.argv[1:]) == 0: 104 | parser.print_help() 105 | parser.exit() 106 | 107 | args = parser.parse_args() 108 | 109 | return args -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ChangeLog 2 | 3 | All notable changes to this project will be kept and updated here. 4 | 5 | **Version 2.1.2** 6 | 7 | **(Nov 6, 2019)** 8 | # Bug Fixes 9 | * Fixed issues with certdb not displaying the full amount of subdomains. 10 | * Fixed a small print line issue to display when searching security trails. 11 | * When searching single sites, fixed issue where subseeker displayed searching all sites. Now it will only display, "searching for", the site you are using to parse subdomains. 12 | * Added ability to list available sites to search in singlesearch. 13 | * Added system exits after searches 14 | 15 | # Changes 16 | * Added verbose mode back in to help with errors. 17 | * Added new search site, securitytrails. (API credentials needed) 18 | * Fixed config file not found. 19 | * Able to display errors now if API credentials are not found. 20 | * Added ability to search keywords with just crtsh. 21 | 22 | **(Nov 2, 2019)** 23 | # Bug Fixes 24 | * Reduced code 25 | * Fixed issue with setup.py not correctly installing alias 26 | * Fixed issue with not finding config file for API when in another directory 27 | 28 | # Changes 29 | * Fixed CLI options. Reduced options, got rid of short options, and made options more simple. 30 | * Added more colors 31 | * Added icons for different status meanings; 32 | successful data will be printed in blue 33 | x No data found 34 | ! Error/Warnings 35 | * Created a basic regex to find domain easier 36 | * Removed verbose mode 37 | 38 | **Version 2.1.1** 39 | 40 | **(Oct 15, 2019)** 41 | # Bug Fixes 42 | * Fixed issue with censys page mode. Censys may or may not of been displaying data due to wrong type mode for page. Issue is corrected now and is working. 43 | 44 | **(Oct 10, 2019)** 45 | # Bug fixes 46 | * Fixed an issue with certspotter function in searchmodes.py, not properly writing results to output file and displaying a "no data found" error. 47 | 48 | # Changes 49 | * Updated config.json to allow Virustotal API 50 | 51 | # Features 52 | * Added Virustotal 53 | * Added ThreatCrowd 54 | 55 | # Future features 56 | * Working on implementing google 57 | 58 | **Version 2.0** 59 | 60 | **(Oct 7, 2019)** 61 | 62 | # Bug fixes 63 | * Corrected issue with JSON return error not printing on screen correctly. 64 | 65 | # Changes 66 | * Changed JSON error response for crtsh 67 | * Added error response for censys, if API credentials are not configured. 68 | 69 | # Features 70 | * Added the ability to use just certspotter, censys, or certdb from searchmodes.py 71 | * Added page option to specify page number for certdb and censys. 72 | * Updated README.md 73 | 74 | **(Oct 5, 2019)** 75 | 76 | # Bug Fixes 77 | * Corrected an issue with useragents not properly returning the correct value. 78 | * Fixed json file and corrected values in searchmodes.py 79 | 80 | # Features 81 | * Certspotter support now added 82 | * Certdb support now added 83 | * Censys support now added. 84 | * API option added to flag options. 85 | 86 | # Future features 87 | **In progress now** 88 | * Ability to use just certspotter, censys, or certdb by itself. 89 | * Adding page option to select different page results on certdb and censys. 90 | 91 | -------------------------------------------------------------------------------- /subseeker_core/subseeker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from subseeker_core.options import options 4 | from subseeker_core.searchmodes import SubSeeker 5 | from subseeker_core.configurations import Process 6 | from subseeker_core.create import GenerateKeywords 7 | from colorama import Fore, Style 8 | import sys 9 | import re 10 | import json 11 | 12 | def main(): 13 | GREEN = Fore.GREEN 14 | CYAN = Fore.CYAN 15 | MAG = Fore.MAGENTA 16 | RED = Fore.RED 17 | YELLOW = Fore.YELLOW 18 | BLUE = Fore.BLUE 19 | RESET = Style.RESET_ALL 20 | 21 | if options().version: 22 | Process().version() 23 | sys.exit(0) 24 | 25 | elif options().generate: 26 | if not options().domain: 27 | print(f"\n{RED}Error: Need domain to parse against!{RESET}\n") 28 | sys.exit(1) 29 | 30 | elif not options().file: 31 | print(f"\n{RED}Error: Need file to generate keywords from!{RESET}\n") 32 | sys.exit(1) 33 | 34 | elif options().file and options().domain: 35 | GenerateKeywords().create() 36 | sys.exit(0) 37 | 38 | elif options().file or options().keywords and not options().singlesearch: 39 | if not options().domain: 40 | print(f"\n{RED}Error: Missing domain.{RESET}\n") 41 | sys.exit(1) 42 | 43 | try: 44 | Process().title() 45 | Process().process() 46 | SubSeeker().thread_execution() 47 | SubSeeker().certspotter() 48 | SubSeeker().certdb() 49 | SubSeeker().threatcrowd() 50 | SubSeeker().censys() 51 | SubSeeker().virustotal() 52 | SubSeeker().securitytrails() 53 | SubSeeker().print_domains() 54 | sys.exit(0) 55 | 56 | except KeyboardInterrupt: 57 | sys.exit(0) 58 | 59 | elif options().singlesearch: 60 | if options().singlesearch.lower() == "options": 61 | print("\tcrtsh\t\tSearch crtsh database.") 62 | print("\tmcrtsh\t\tSearch crtsh database using keywords.") 63 | print("\tcertspotter\tSearch certspotter database.") 64 | print("\tcertdb\t\tSearch certdb database.") 65 | print("\tcensys\t\tSearch censys database.") 66 | print("\tvirustotal\tSearch virustotal database.") 67 | print("\tthreatcrowd\tSearch threatcrowd database.") 68 | print("\tsecuritytrails\tSearch securitytrails database.") 69 | 70 | if not options().domain and not options().singlesearch == "options": 71 | print(f"\n{RED}Error: Missing domain.{RESET}\n") 72 | sys.exit(1) 73 | 74 | if options().singlesearch.lower() == "crtsh": 75 | Process().title() 76 | print(f"{MAG}[~]{CYAN} Checking Crtsh...{RESET}\n") 77 | SubSeeker().crtsh() 78 | SubSeeker().print_domains() 79 | sys.exit(0) 80 | 81 | elif options().singlesearch.lower() == "certspotter": 82 | Process().title() 83 | print(f"{MAG}[~]{CYAN} Checking Certspotter...{RESET}\n") 84 | SubSeeker().certspotter() 85 | SubSeeker().print_domains() 86 | sys.exit(0) 87 | 88 | elif options().singlesearch.lower() == "certdb": 89 | Process().title() 90 | print(f"{MAG}[~]{CYAN} Checking CertDB...{RESET}\n") 91 | SubSeeker().certdb() 92 | SubSeeker().print_domains() 93 | sys.exit(0) 94 | 95 | elif options().singlesearch.lower() == "threatcrowd": 96 | Process().title() 97 | print(f"{MAG}[~]{CYAN} Checking threatcrowd...{RESET}\n") 98 | SubSeeker().threatcrowd() 99 | SubSeeker().print_domains() 100 | sys.exit(0) 101 | 102 | elif options().singlesearch.lower() == "censys": 103 | Process().title() 104 | print(f"{MAG}[~]{CYAN} Checking Censys...{RESET}\n") 105 | SubSeeker().censys() 106 | SubSeeker().print_domains() 107 | sys.exit(0) 108 | 109 | elif options().singlesearch.lower() == "virustotal": 110 | Process().title() 111 | print(f"{MAG}[~]{CYAN} Checking VirusTotal...{RESET}\n") 112 | SubSeeker().virustotal() 113 | SubSeeker().print_domains() 114 | sys.exit(0) 115 | 116 | elif options().singlesearch.lower() == "securitytrails": 117 | Process().title() 118 | print(f"{MAG}[~]{CYAN} Checking SecurityTrails...{RESET}\n") 119 | SubSeeker().securitytrails() 120 | SubSeeker().print_domains() 121 | sys.exit(0) 122 | 123 | elif options().singlesearch.lower() == "mcrtsh": 124 | if options().file or options().keywords and options().domain: 125 | Process().title() 126 | print(f"{MAG}[~]{CYAN} Checking Crtsh...{RESET}\n") 127 | SubSeeker().thread_execution() 128 | SubSeeker().print_domains() 129 | sys.exit(0) 130 | 131 | elif not options().file or not options().keywords: 132 | print(f"\n{RED}Error! Need --file with list of keywords or at least one --keyword.\n") 133 | sys.exit(1) 134 | 135 | else: 136 | if not options().domain: 137 | print(f"\n{RED}Error: Missing domain.{RESET}\n") 138 | sys.exit(1) 139 | 140 | try: 141 | Process().title() 142 | Process().process() 143 | SubSeeker().crtsh() 144 | SubSeeker().certspotter() 145 | SubSeeker().certdb() 146 | SubSeeker().threatcrowd() 147 | SubSeeker().censys() 148 | SubSeeker().virustotal() 149 | SubSeeker().securitytrails() 150 | SubSeeker().print_domains() 151 | sys.exit(0) 152 | 153 | except KeyboardInterrupt: 154 | sys.exit(0) 155 | 156 | 157 | if __name__ == "__main__": 158 | main() 159 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 |
206 |
--------------------------------------------------------------------------------
/subseeker_core/searchmodes.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import re
3 | import json
4 | import os
5 | import sys
6 | import concurrent.futures
7 | import subseeker_core.useragents
8 | import platform
9 | import subprocess
10 | from subseeker_core.options import options
11 | from colorama import Fore, Style
12 |
13 | class SubSeeker():
14 | GREEN = Fore.GREEN
15 | RED = Fore.RED
16 | YELLOW = Fore.YELLOW
17 | BLUE = Fore.BLUE
18 | WHITE = Fore.WHITE
19 | RESET = Style.RESET_ALL
20 |
21 | if options().domain:
22 | # domain_regex = r"([^\.]*.(?=com).+)"
23 | domain_regex = r"[a-zA-Z0-9].*"
24 | domain = re.findall(domain_regex, options().domain)[0]
25 |
26 | # if user uses api, search for config file, that way no matter what directory user is in,
27 | # subseeker can find config file.
28 | if options().api:
29 |
30 | if options().verbose:
31 | print(f"\n{YELLOW}[!] Looking for config file containing API credentials...{RESET}")
32 |
33 | for root, dirs, files in os.walk("/"):
34 | filename = "subseeker_config.json"
35 | if filename in files:
36 | config_file = os.path.join(root, filename)
37 |
38 | if options().verbose:
39 | print(f"{GREEN}[+] Config file found: {config_file}{RESET}")
40 | print(f"{GREEN}[+] Starting search...{RESET}\n")
41 |
42 |
43 |
44 | domains = set()
45 |
46 | # Default value for API key is false, if user does not have one.
47 | def __init__(self, apikey=False, params=None, page=1):
48 | self.apikey = apikey
49 | self.params = params
50 | self.page = page
51 | self.threads = 20
52 |
53 | def crtsh(self):
54 | org_domain = options().domain.replace("*", "%25")
55 | url = f"https://crt.sh/?q={org_domain}&output=json"
56 |
57 | try:
58 | response = requests.get(url, headers={'User-Agent':subseeker_core.useragents.useragent()})
59 | regex = r'[^%*].*'
60 | data = response.json()
61 |
62 | if data:
63 | for row in data:
64 | row = re.findall(regex, row["name_value"])[0]
65 | self.domains.add(row)
66 |
67 | elif not data:
68 | print(f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}crtsh.{self.RESET}")
69 |
70 | except ValueError:
71 | pass
72 |
73 | def certspotter(self):
74 | try:
75 | if options().api:
76 | with open(self.config_file, "r") as f:
77 |
78 | jfile = json.load(f)
79 | self.apikey = jfile["API_INFO"][0]["key"]
80 |
81 | if self.apikey == "":
82 | print(f"{self.YELLOW}[!] API credentials not found for {self.WHITE}certspotter.{self.RESET}")
83 |
84 | else:
85 | params = {'Authorization': 'Bearer ' + self.apikey}
86 |
87 | url = f"https://api.certspotter.com/v1/issuances?domain={self.domain}&include_subdomains=true&expand=dns_names&expand=cert"
88 | response = requests.get(url, params=self.params, headers={'User-Agent':subseeker_core.useragents.useragent()})
89 | data = response.json()
90 |
91 | if data:
92 | for row in data:
93 | row = row["dns_names"]
94 |
95 | for domain in row:
96 | self.domains.add(row)
97 |
98 | elif not data:
99 | print(f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}certspotter.{self.RESET}")
100 |
101 | except TypeError as e:
102 | pass
103 |
104 | except IndexError:
105 | pass
106 |
107 | except KeyError:
108 | pass
109 |
110 | except ValueError:
111 | pass
112 |
113 | except AttributeError:
114 | print(f"{self.YELLOW}[!] Config file not found!{self.RESET}")
115 | print(f"{self.YELLOW}[!] System exiting now. Fix config file or run without --api command.{self.RESET}\n")
116 | sys.exit(1)
117 |
118 | def certdb(self):
119 | if options().page:
120 | page = options().page
121 |
122 | else:
123 | page = self.page
124 |
125 | try:
126 | # Default false
127 | if options().api:
128 | with open(self.config_file, "r") as f:
129 | jfile = json.load(f)
130 | self.apikey = jfile["API_INFO"][1]["key"]
131 |
132 | if self.apikey == "":
133 | print(f"{self.YELLOW}[!] API credentials not found for {self.WHITE}certdb.{self.RESET}")
134 |
135 | url = f"https://api.spyse.com/v1/subdomains?api_token={self.apikey}&domain={self.domain}&page={page}"
136 |
137 | response = requests.get(url, headers={'User-Agent':subseeker_core.useragents.useragent()})
138 | data = response.json()
139 |
140 | if data:
141 | for row in data["records"]:
142 | subdomains = row["domain"]
143 | self.domains.add(subdomains)
144 |
145 | elif not data:
146 | print(f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}certdb. {self.YELLOW}Page: {page}{self.RESET}")
147 |
148 | except KeyError:
149 | pass
150 |
151 | except IndexError:
152 | pass
153 |
154 | except ValueError:
155 | pass
156 |
157 | except AttributeError:
158 | print(f"{self.YELLOW}[!] Config file not found!{self.RESET}")
159 | print(f"{self.YELLOW}[!] System exiting now. Fix config file or run without --api command.{self.RESET}\n")
160 | sys.exit(1)
161 |
162 | def censys(self):
163 | if options().page:
164 | page = options().page
165 |
166 | else:
167 | page = self.page
168 |
169 | try:
170 | if options().api:
171 | with open(self.config_file, "r") as f:
172 | jfile = json.load(f)
173 | self.apikey = jfile["API_INFO"][2]["id"]
174 | secret = jfile["API_INFO"][2]["secret"]
175 |
176 | if self.apikey == "" or secret == "":
177 | print(f"{self.YELLOW}[!] API credentials not found for {self.WHITE}censys.{self.RESET}")
178 |
179 | api_url = "https://censys.io/api/v1/search/certificates"
180 |
181 | regex = r'(?:CN=).+'
182 | params = {"query":f"{self.domain}", "page":page}
183 |
184 | response = requests.post(api_url, json=params, auth=(self.apikey, secret), headers={'User-Agent':subseeker_core.useragents.useragent()})
185 | data = response.json()
186 |
187 | if data:
188 | for row in data["results"]:
189 | CN = row["parsed.subject_dn"].splitlines()
190 |
191 | for line in CN:
192 | line = re.findall(regex, line)[0][3:]
193 | self.domains.add(line)
194 |
195 | elif not data:
196 | print(f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}censys. {self.YELLOW}Page: {page}{self.RESET}")
197 |
198 | elif not options().api:
199 | if options().verbose:
200 | print(f"{self.YELLOW}[!] API credentials needed for {self.WHITE}censys.{self.RESET}")
201 | pass
202 |
203 | elif not options().api:
204 | pass
205 |
206 |
207 | except IndexError:
208 | pass
209 |
210 | except KeyError:
211 | pass
212 |
213 | except ValueError:
214 | pass
215 |
216 | except AttributeError:
217 | print(f"{self.YELLOW}[!] Config file not found!{self.RESET}")
218 | print(f"{self.YELLOW}[!] System exiting now. Fix config file or run without --api command.{self.RESET}\n")
219 | sys.exit(1)
220 |
221 | def virustotal(self):
222 | try:
223 | if options().api:
224 | with open(self.config_file, "r") as f:
225 | jfile = json.load(f)
226 | self.apikey = jfile["API_INFO"][3]["key"]
227 |
228 | if self.apikey == "":
229 | print(f"{self.YELLOW}[!] API credentials not found for {self.WHITE}virustotal.{self.RESET}")
230 |
231 | api_url = "https://www.virustotal.com/vtapi/v2/domain/report"
232 | params = {"apikey":f"{self.apikey}", "domain":f"{self.domain}"}
233 |
234 | response = requests.get(api_url, params=params, headers={'User-Agent':subseeker_core.useragents.useragent()})
235 |
236 | data = response.json()
237 |
238 | if data:
239 | subdomains = "\n".join(data["subdomains"])
240 | self.domains.add(subdomains)
241 |
242 | elif not data:
243 | print(f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}virustotal.{self.RESET}")
244 |
245 | elif not options().api:
246 | if options().verbose:
247 | print(f"{self.YELLOW}[!] API credentails needed for {self.WHITE}virustotal.{self.RESET}")
248 | pass
249 |
250 | elif not options().api:
251 | pass
252 |
253 | except KeyError:
254 | pass
255 |
256 | except IndexError:
257 | pass
258 |
259 | except ValueError:
260 | pass
261 |
262 | except AttributeError:
263 | print(f"{self.YELLOW}[!] Config file not found!{self.RESET}")
264 | print(f"{self.YELLOW}[!] System exiting now. Fix config file or run without --api command.{self.RESET}\n")
265 | sys.exit(1)
266 |
267 | def threatcrowd(self):
268 | try:
269 | api_url = "http://www.threatcrowd.org/searchApi/v2/domain/report/"
270 | params = {"domain":f"{self.domain}"}
271 |
272 | response = requests.get(api_url, params=params, headers={'User-Agent':subseeker_core.useragents.useragent()})
273 |
274 | data = json.loads(response.text)
275 | data = data["subdomains"]
276 |
277 | if data:
278 | for row in data:
279 | self.domains.add(row)
280 |
281 | elif not data:
282 | print(f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}threatcrowd.{self.RESET}")
283 |
284 | except KeyError:
285 | pass
286 |
287 | except IndexError:
288 | pass
289 |
290 | except ValueError:
291 | pass
292 |
293 | def securitytrails(self):
294 | try:
295 | if options().api:
296 | with open(self.config_file, "r") as f:
297 | jfile = json.load(f)
298 | self.apikey = jfile["API_INFO"][4]["key"]
299 |
300 | if self.apikey == "":
301 | print(f"{self.YELLOW}[!] API credentials not found for {self.WHITE}securitytrails.{self.RESET}")
302 |
303 | api_url = f"https://api.securitytrails.com/v1/domain/{self.domain}/subdomains"
304 | params = {"apikey":f"{self.apikey}"}
305 | response = requests.get(api_url, params=params, headers={'User-Agent':subseeker_core.useragents.useragent()})
306 | data = json.loads(response.text)
307 |
308 | if data:
309 | for row in data["subdomains"]:
310 | row = f"{row}.{self.domain}"
311 | self.domains.add(row)
312 |
313 | elif not data:
314 | print(f"{self.RED}[x] No data found for {options().domain} using {self.WHITE}securitytrails.{self.RESET}")
315 |
316 | elif not options().api:
317 | if options().verbose:
318 | print(f"{self.YELLOW}[!] API credentials needed for {self.WHITE}securitytrails.{self.RESET}")
319 | pass
320 |
321 | elif not options().api:
322 | pass
323 |
324 | except KeyError as e:
325 | pass
326 |
327 | except IndexError as e:
328 | pass
329 |
330 | except ValueError as e:
331 | pass
332 |
333 | except AttributeError:
334 | print(f"{self.YELLOW}[!] Config file not found!{self.RESET}")
335 | print(f"{self.YELLOW}[!] System exiting now. Fix config file or run without --api command.{self.RESET}\n")
336 | sys.exit(1)
337 |
338 |
339 | def thread_execution(self):
340 | domains = []
341 |
342 | if options().file:
343 | with open(options().file, "r") as f:
344 | for sub in f:
345 |
346 | if sub == "":
347 | pass
348 | else:
349 | sub = str(sub.strip("\n"))
350 | domains.append(sub)
351 |
352 | elif options().keywords:
353 | for sub in options().keywords:
354 | domains.append(sub)
355 |
356 | if options().threads:
357 | threads = options().threads
358 | else:
359 | threads = self.threads
360 |
361 | with concurrent.futures.ThreadPoolExecutor(max_workers=threads) as executor:
362 | executor.map(self.multi_keyword_search, domains)
363 |
364 | # Parse crt.sh for each sub domain keyword
365 | def multi_keyword_search(self, sub):
366 | try:
367 | url = f"https://crt.sh/?q=%25{sub}%25.{self.domain}&output=json"
368 | response = requests.get(url, headers={'User-Agent':subseeker_core.useragents.useragent()})
369 | regex = r'[^%*].*'
370 | data = response.json()
371 |
372 | if data:
373 | for row in data:
374 | row = re.findall(regex, row["name_value"])[0]
375 | self.domains.add(row)
376 |
377 | elif not data:
378 | print(f"{self.RED}[x] No data found:{self.WHITE} {sub}{self.RESET}")
379 |
380 | # JSON Decode Error
381 | except ValueError:
382 | print(f"{self.YELLOW}[!]{self.RED} JSON value error:{self.WHITE} {sub}{self.RESET}")
383 | pass
384 |
385 | def print_domains(self):
386 | subdomains = "\n".join(self.domains)
387 | print(f"{self.BLUE}{subdomains}{self.RESET}")
388 |
389 | if options().out:
390 | with open(options().out, "a") as f:
391 | f.write(str(subdomains))
392 | f.write("\n")
393 |
--------------------------------------------------------------------------------