├── images ├── README.md ├── banner.png └── demo.jpeg ├── README.md ├── sholister_org.py └── sholister_hostname.py /images/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eslam3kl/ShoLister/HEAD/images/banner.png -------------------------------------------------------------------------------- /images/demo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eslam3kl/ShoLister/HEAD/images/demo.jpeg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ShoLister 2 | ShoLister is a tool that collects all available subdomains for specific hostname or organization from Shodan. The tool is designed to be used from Penetration Tester and Bug Bounty Hunters. 3 |

4 | 5 |

6 | 7 | 8 | ### Requirments/Install 9 | 1. Shodan paid account. 10 | 2. Python3 11 | 12 | ``` 13 | pip install shodan 14 | pip install termcolor 15 | shodan init YOUR_API_KEY 16 | ``` 17 | 18 | ### Usage 19 | -> You have 2 python scripts, the first one will search for your scope subdomains using 2 filters **hostname** and **Ssl.cert.subject.CN** The second script will search using filter **org** 20 | 21 | **sholister_hostname.py** 22 | ``` 23 | > scope_domains.txt example: 24 | yahoo.com 25 | uber.com 26 | twitter.com 27 | 28 | > Run: 29 | python3 sholister_hostname.py scope_domains.txt 30 | ``` 31 | 32 | **sholister_org.py** 33 | ``` 34 | > scope_organizations.txt example: 35 | Google LLC 36 | Uber Technologies LLC 37 | Twitter 38 | 39 | > Run: 40 | python3 sholister_org.py scope_organizations.txt 41 | ``` 42 | ![demo](https://github.com/eslam3kl/ShoLister/blob/main/images/demo.jpeg) 43 | 44 | 45 | ### Differences between ShoLister and Shodan CLI 46 | 1. ShoLister based on Shodan Library so they're using the same gateway to get the results. 47 | 2. Filter the results to avoid ISP false positive domains. 48 | 3. You can pass a file with multiple hosts or organizations names to make it more easier to get the results. 49 | 4. ShoLister provide the results as a separate file for each hostname or organization. 50 | 51 | ---------------------------------- 52 | ### Stay in touch <3 53 | [LinkedIn](https://www.linkedin.com/in/eslam3kl/) | [Blog](https://eslam3kl.medium.com/) | [Twitter](https://twitter.com/eslam3kll) 54 | -------------------------------------------------------------------------------- /sholister_org.py: -------------------------------------------------------------------------------- 1 | from shodan import Shodan 2 | from shodan.cli.helpers import get_api_key 3 | from termcolor import colored 4 | import sys 5 | 6 | header1 = r''' 7 | _ _ _ _ _ _ _ _ _ 8 | / \ / \ / \ / \ / \ / \ / \ / \ / \ 9 | ( s | h | o | l | i | s | t | e | r ) 10 | \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ v0.1 11 | ''' 12 | header2 = r''' 13 | Hostname search using [Org] Filter. 14 | Coded By: Eslam Akl (@eslam3kll) 15 | ''' 16 | print(colored(header1, 'red', attrs=['bold'])) 17 | print(colored(header2, 'white', attrs=['bold'])) 18 | main_domains_file = sys.argv[1] 19 | api = Shodan(get_api_key()) 20 | limit = 1000 21 | counter = 0 22 | results = [] 23 | with open(main_domains_file, 'r') as domains: 24 | try: 25 | for line in domains: 26 | line = line.strip() 27 | filename = line + '_shodan.txt' 28 | f = open(filename, 'a+') 29 | print(colored("[+] Searching for: ", 'green') + line) 30 | 31 | # org search 32 | org_query = 'org:"' + line + '" 200' 33 | for banner in api.search_cursor(org_query): 34 | for hostname in banner['hostnames']: 35 | results.append(hostname) 36 | counter += 1 37 | if counter >= limit: 38 | break 39 | 40 | results_length = len(results) 41 | print(colored("-> Found " + str(results_length) + " unique result for Organization ["+ line + "] responds with status code [200 OK]", 'cyan')) 42 | print(colored("-> Output file name: " + filename, 'cyan') + "\n") 43 | # get results to output file 44 | for line in results: 45 | f.write(line + "\n") 46 | f.close() 47 | results = [] 48 | 49 | 50 | except KeyboardInterrupt: 51 | print(colored("\nKeyboardInterrupt detected! GoodBye", 'red')) 52 | pass 53 | -------------------------------------------------------------------------------- /sholister_hostname.py: -------------------------------------------------------------------------------- 1 | from shodan import Shodan 2 | from shodan.cli.helpers import get_api_key 3 | from termcolor import colored 4 | import sys 5 | 6 | header1 = r''' 7 | _ _ _ _ _ _ _ _ _ 8 | / \ / \ / \ / \ / \ / \ / \ / \ / \ 9 | ( s | h | o | l | i | s | t | e | r ) 10 | \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ v0.1 11 | ''' 12 | header2 = r''' 13 | Hostname search using [hostname] and [Ssl.cert.subject.CN] Filters. 14 | Coded By: Eslam Akl (@eslam3kll) 15 | ''' 16 | print(colored(header1, 'red', attrs=['bold'])) 17 | print(colored(header2, 'white', attrs=['bold'])) 18 | main_domains_file = sys.argv[1] 19 | api = Shodan(get_api_key()) 20 | limit = 1000 21 | counter = 0 22 | results = [] 23 | with open(main_domains_file, 'r') as domains: 24 | try: 25 | for line in domains: 26 | line = line.strip() 27 | filename = line + '_shodan.txt' 28 | f = open(filename, 'a+') 29 | print(colored("[+] Searching for: ", 'green') + line) 30 | 31 | # ssl search 32 | ssl_query = 'Ssl.cert.subject.CN:"' + line + '" 200' 33 | for banner in api.search_cursor(ssl_query): 34 | for hostname in banner['hostnames']: 35 | #print(hostname) 36 | if line in hostname: 37 | results.append(hostname) 38 | counter += 1 39 | if counter >= limit: 40 | break 41 | # hostname search 42 | hostname_query = 'hostname:"' + line + '" 200' 43 | for banner in api.search_cursor(hostname_query): 44 | for hostname in banner['hostnames']: 45 | #print(hostname) 46 | if line in hostname: 47 | if hostname not in results: 48 | results.append(hostname) 49 | counter += 1 50 | if counter >= limit: 51 | break 52 | results_length = len(results) 53 | print(colored("-> Found " + str(results_length) + " unique result for subdomain ["+ line + "] responds with status code [200 OK]", 'cyan')) 54 | print(colored("-> Output file name: " + filename, 'cyan') + "\n") 55 | # get results to output file 56 | for line in results: 57 | f.write(line + "\n") 58 | f.close() 59 | results = [] 60 | 61 | except KeyboardInterrupt: 62 | print(colored("\nKeyboardInterrupt detected! GoodBye", 'red')) 63 | pass 64 | 65 | 66 | --------------------------------------------------------------------------------