diff --git a/README.md b/README.md index c860b29..7e1203c 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,15 @@ -## About Sublist3r +# About Sublist3r Sublist3r is a python tool designed to enumerate subdomains of websites using OSINT. It helps penetration testers and bug hunters collect and gather subdomains for the domain they are targeting. Sublist3r enumerates subdomains using many search engines such as Google, Yahoo, Bing, Baidu and Ask. Sublist3r also enumerates subdomains using Netcraft, Virustotal, ThreatCrowd, DNSdumpster and ReverseDNS. [subbrute](https://github.com/TheRook/subbrute) was integrated with Sublist3r to increase the possibility of finding more subdomains using bruteforce with an improved wordlist. The credit goes to TheRook who is the author of subbrute. +## Key Updates/Changes + +* Added `--silent`,`-s` flag to handle unix-like output piping +* Added Hackertarget & Shodan Enumeraton of Domain +* Removed Defunct Virustotal API + ## Screenshots ![Sublist3r](http://www.secgeek.net/images/Sublist3r.png "Sublist3r in action") diff --git a/requirements.txt b/requirements.txt index 498ea9d..96547b8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ argparse dnspython requests +bs4 +requests[socks] diff --git a/setup.py b/setup.py index eb2383c..66746eb 100644 --- a/setup.py +++ b/setup.py @@ -2,12 +2,12 @@ from setuptools import setup, find_packages setup( name='Sublist3r', - version='1.0', - python_requires='>=2.7', - install_requires=['dnspython', 'requests', 'argparse; python_version==\'2.7\''], + version='2.1', + python_requires='>=3.10', + install_requires=['bs4','dnspython', 'requests', 'argparse; python_version==\'3.10\''], packages=find_packages()+['.'], include_package_data=True, - url='https://github.com/aboul3la/Sublist3r', + url='https://github.com/fmjal/Sublist3r', license='GPL-2.0', description='Subdomains enumeration tool for penetration testers', classifiers=[ @@ -25,6 +25,8 @@ setup( 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', 'Topic :: Security', ], keywords='subdomain dns detection', diff --git a/sublist3r.py b/sublist3r.py old mode 100755 new mode 100644 index 760e5ce..0be8ed1 --- a/sublist3r.py +++ b/sublist3r.py @@ -1,8 +1,8 @@ -#!/usr/bin/env python +#!/usr/bin/env python4 # coding: utf-8 # Sublist3r v1.0 # By Ahmed Aboul-Ela - twitter.com/aboul3la - +# And fmjal - github.com/fmjal # modules in standard library import re import sys @@ -16,6 +16,7 @@ import threading import socket import json from collections import Counter +from bs4 import BeautifulSoup # external modules from subbrute import subbrute @@ -79,7 +80,9 @@ def banner(): ___) | |_| | |_) | | \__ \ |_ ___) | | |____/ \__,_|_.__/|_|_|___/\__|____/|_|%s%s - # Coded By Ahmed Aboul-Ela - @aboul3la + # Coded By: + # - Ahmed Aboul-Ela - @aboul3la + # - fmjal - @fmjal """ % (R, W, Y)) @@ -103,6 +106,7 @@ def parse_args(): parser.add_argument('-e', '--engines', help='Specify a comma-separated list of search engines') parser.add_argument('-o', '--output', help='Save the results to text file') parser.add_argument('-n', '--no-color', help='Output without color', default=False, action='store_true') + parser.add_argument("-s","--silent",default=False,action='store_true',help='Run without showing a banner or status updates') return parser.parse_args() @@ -850,6 +854,84 @@ class PassiveDNS(enumratorBaseThreaded): except Exception as e: pass +class HTEnum(enumratorBaseThreaded): + def __init__(self, domain, subdomains=None, q=None, silent=False, verbose=True): + subdomains = subdomains or [] + base_url = 'https://api.hackertarget.com/hostsearch/?q={domain}' + self.engine_name = "HTEnum" + self.q = q + super(HTEnum, self).__init__(base_url, self.engine_name, domain, subdomains, q=q, silent=silent, verbose=verbose) + return + + def req(self, url): + try: + resp = self.session.get(url, headers=self.headers, timeout=self.timeout) + except Exception as e: + resp = None + + return self.get_response(resp) + + def get_response(self, resp): + if resp and resp.status_code == 200 and not resp.text.startswith("API Count"): + return resp.text + return None + + def extract_domains(self, resp): + for line in resp.splitlines(): + subdomain = line.split(',')[0] + if subdomain and subdomain not in self.subdomains: + self.subdomains.append(subdomain) + + def enumerate(self): + url = self.base_url.format(domain=self.domain) + resp = self.req(url) + if not resp: + return self.subdomains + + self.extract_domains(resp) + return self.subdomains + +class ShodanEnum(enumratorBaseThreaded): + def __init__(self, domain, subdomains=None, q=None, silent=False, verbose=True): + self.domain=domain + subdomains = subdomains or [] + base_url = 'https://www.shodan.io/domain/{domain}' + self.engine_name = "Shodan" + self.q = q + super(ShodanEnum, self).__init__(base_url, self.engine_name, domain, subdomains, q=q, silent=silent, verbose=verbose) + + def req(self, url): + try: + resp = self.session.get(url, headers=self.headers, timeout=self.timeout) + except Exception as e: + resp = None + + return self.get_response(resp) + + def get_response(self, resp): + if resp and resp.status_code == 200: + return resp.text + return None + + def extract_domains(self, resp): + soup = BeautifulSoup(resp, 'html.parser') + subdomains_list = soup.find('ul', id='subdomains') + if subdomains_list: + for li in subdomains_list.find_all('li'): + subdomain = li.get_text().strip() + if subdomain and subdomain not in self.subdomains: + if subdomain != "*" and subdomain != "_dmarc": + self.subdomains.append(f'{subdomain}.{self.domain}') + + def enumerate(self): + url = self.base_url.format(domain=self.domain) + resp = self.req(url) + if not resp: + return self.subdomains + + self.extract_domains(resp) + return self.subdomains + class portscan(): def __init__(self, subdomains, ports): @@ -922,7 +1004,9 @@ def main(domain, threads, savefile, ports, silent, verbose, enable_bruteforce, e 'virustotal': Virustotal, 'threatcrowd': ThreatCrowd, 'ssl': CrtSearch, - 'passivedns': PassiveDNS + 'passivedns': PassiveDNS, + "HTEnum":HTEnum, + "Shodan":ShodanEnum } chosenEnums = [] @@ -930,8 +1014,8 @@ def main(domain, threads, savefile, ports, silent, verbose, enable_bruteforce, e if engines is None: chosenEnums = [ BaiduEnum, YahooEnum, GoogleEnum, BingEnum, AskEnum, - NetcraftEnum, DNSdumpster, Virustotal, ThreatCrowd, - CrtSearch, PassiveDNS + NetcraftEnum, DNSdumpster, ThreatCrowd, + CrtSearch, PassiveDNS,HTEnum,ShodanEnum ] else: engines = engines.split(',') @@ -980,9 +1064,8 @@ def main(domain, threads, savefile, ports, silent, verbose, enable_bruteforce, e pscan = portscan(subdomains, ports) pscan.run() - elif not silent: - for subdomain in subdomains: - print(G + subdomain + W) + for subdomain in subdomains: + print(G + subdomain + W) return subdomains @@ -995,12 +1078,14 @@ def interactive(): enable_bruteforce = args.bruteforce verbose = args.verbose engines = args.engines + silent=args.silent if verbose or verbose is None: verbose = True if args.no_color: no_color() - banner() - res = main(domain, threads, savefile, ports, silent=False, verbose=verbose, enable_bruteforce=enable_bruteforce, engines=engines) + if not silent: + banner() + res = main(domain, threads, savefile, ports, silent=args.silent, verbose=verbose, enable_bruteforce=enable_bruteforce, engines=engines) if __name__ == "__main__": interactive()