(.*?)', re.S)
659 | link_regex = re.compile('(.*?) ', re.S)
660 | links = []
661 | try:
662 | results_tbl = tbl_regex.findall(resp)[0]
663 | except IndexError:
664 | results_tbl = ''
665 | links_list = link_regex.findall(results_tbl)
666 | links = list(set(links_list))
667 | for link in links:
668 | subdomain = link.strip()
669 | if not subdomain.endswith(self.domain):
670 | continue
671 | if subdomain and subdomain not in self.subdomains and subdomain != self.domain:
672 | self.subdomains.append(subdomain.strip())
673 | return links
674 |
675 |
676 | class Virustotal(enumratorBaseThreaded):
677 | def __init__(self, domain, subdomains=None, q=None, silent=False, verbose=True):
678 | subdomains = subdomains or []
679 | base_url = 'https://www.virustotal.com/ui/domains/{domain}/subdomains'
680 | self.engine_name = "Virustotal"
681 | self.q = q
682 | super(Virustotal, self).__init__(base_url, self.engine_name, domain, subdomains, q=q, silent=silent, verbose=verbose)
683 | self.url = self.base_url.format(domain=self.domain)
684 | return
685 |
686 | # the main send_req need to be rewritten
687 | def send_req(self, url):
688 | try:
689 | resp = self.session.get(url, headers=self.headers, timeout=self.timeout)
690 | except Exception as e:
691 | self.print_(e)
692 | resp = None
693 |
694 | return self.get_response(resp)
695 |
696 | # once the send_req is rewritten we don't need to call this function, the stock one should be ok
697 | def enumerate(self):
698 | while self.url != '':
699 | resp = self.send_req(self.url)
700 | resp = json.loads(resp)
701 | if 'error' in resp:
702 | self.print_(R + "[!] Error: Virustotal probably now is blocking our requests" + W)
703 | break
704 | if 'links' in resp and 'next' in resp['links']:
705 | self.url = resp['links']['next']
706 | else:
707 | self.url = ''
708 | self.extract_domains(resp)
709 | return self.subdomains
710 |
711 | def extract_domains(self, resp):
712 | #resp is already parsed as json
713 | try:
714 | for i in resp['data']:
715 | if i['type'] == 'domain':
716 | subdomain = i['id']
717 | if not subdomain.endswith(self.domain):
718 | continue
719 | if subdomain not in self.subdomains and subdomain != self.domain:
720 | if self.verbose:
721 | self.print_("%s%s: %s%s" % (R, self.engine_name, W, subdomain))
722 | self.subdomains.append(subdomain.strip())
723 | except Exception:
724 | pass
725 |
726 |
727 | class ThreatCrowd(enumratorBaseThreaded):
728 | def __init__(self, domain, subdomains=None, q=None, silent=False, verbose=True):
729 | subdomains = subdomains or []
730 | base_url = 'https://www.threatcrowd.org/searchApi/v2/domain/report/?domain={domain}'
731 | self.engine_name = "ThreatCrowd"
732 | self.q = q
733 | super(ThreatCrowd, self).__init__(base_url, self.engine_name, domain, subdomains, q=q, silent=silent, verbose=verbose)
734 | return
735 |
736 | def req(self, url):
737 | try:
738 | resp = self.session.get(url, headers=self.headers, timeout=self.timeout)
739 | except Exception:
740 | resp = None
741 |
742 | return self.get_response(resp)
743 |
744 | def enumerate(self):
745 | url = self.base_url.format(domain=self.domain)
746 | resp = self.req(url)
747 | self.extract_domains(resp)
748 | return self.subdomains
749 |
750 | def extract_domains(self, resp):
751 | try:
752 | links = json.loads(resp)['subdomains']
753 | for link in links:
754 | subdomain = link.strip()
755 | if not subdomain.endswith(self.domain):
756 | continue
757 | if subdomain not in self.subdomains and subdomain != self.domain:
758 | if self.verbose:
759 | self.print_("%s%s: %s%s" % (R, self.engine_name, W, subdomain))
760 | self.subdomains.append(subdomain.strip())
761 | except Exception as e:
762 | pass
763 |
764 |
765 | class CrtSearch(enumratorBaseThreaded):
766 | def __init__(self, domain, subdomains=None, q=None, silent=False, verbose=True):
767 | subdomains = subdomains or []
768 | base_url = 'https://crt.sh/?q=%25.{domain}'
769 | self.engine_name = "SSL Certificates"
770 | self.q = q
771 | super(CrtSearch, self).__init__(base_url, self.engine_name, domain, subdomains, q=q, silent=silent, verbose=verbose)
772 | return
773 |
774 | def req(self, url):
775 | try:
776 | resp = self.session.get(url, headers=self.headers, timeout=self.timeout)
777 | except Exception:
778 | resp = None
779 |
780 | return self.get_response(resp)
781 |
782 | def enumerate(self):
783 | url = self.base_url.format(domain=self.domain)
784 | resp = self.req(url)
785 | if resp:
786 | self.extract_domains(resp)
787 | return self.subdomains
788 |
789 | def extract_domains(self, resp):
790 | link_regx = re.compile(' | (.*?) | ')
791 | try:
792 | links = link_regx.findall(resp)
793 | for link in links:
794 | link = link.strip()
795 | subdomains = []
796 | if '
' in link:
797 | subdomains = link.split('
')
798 | else:
799 | subdomains.append(link)
800 |
801 | for subdomain in subdomains:
802 | if not subdomain.endswith(self.domain) or '*' in subdomain:
803 | continue
804 |
805 | if '@' in subdomain:
806 | subdomain = subdomain[subdomain.find('@')+1:]
807 |
808 | if subdomain not in self.subdomains and subdomain != self.domain:
809 | if self.verbose:
810 | self.print_("%s%s: %s%s" % (R, self.engine_name, W, subdomain))
811 | self.subdomains.append(subdomain.strip())
812 | except Exception as e:
813 | print(e)
814 | pass
815 |
816 | class PassiveDNS(enumratorBaseThreaded):
817 | def __init__(self, domain, subdomains=None, q=None, silent=False, verbose=True):
818 | subdomains = subdomains or []
819 | base_url = 'https://api.sublist3r.com/search.php?domain={domain}'
820 | self.engine_name = "PassiveDNS"
821 | self.q = q
822 | super(PassiveDNS, self).__init__(base_url, self.engine_name, domain, subdomains, q=q, silent=silent, verbose=verbose)
823 | return
824 |
825 | def req(self, url):
826 | try:
827 | resp = self.session.get(url, headers=self.headers, timeout=self.timeout)
828 | except Exception as e:
829 | resp = None
830 |
831 | return self.get_response(resp)
832 |
833 | def enumerate(self):
834 | url = self.base_url.format(domain=self.domain)
835 | resp = self.req(url)
836 | if not resp:
837 | return self.subdomains
838 |
839 | self.extract_domains(resp)
840 | return self.subdomains
841 |
842 | def extract_domains(self, resp):
843 | try:
844 | subdomains = json.loads(resp)
845 | for subdomain in subdomains:
846 | if subdomain not in self.subdomains and subdomain != self.domain:
847 | if self.verbose:
848 | self.print_("%s%s: %s%s" % (R, self.engine_name, W, subdomain))
849 | self.subdomains.append(subdomain.strip())
850 | except Exception as e:
851 | pass
852 |
853 |
854 | class portscan():
855 | def __init__(self, subdomains, ports):
856 | self.subdomains = subdomains
857 | self.ports = ports
858 | self.lock = None
859 |
860 | def port_scan(self, host, ports):
861 | openports = []
862 | self.lock.acquire()
863 | for port in ports:
864 | try:
865 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
866 | s.settimeout(2)
867 | result = s.connect_ex((host, int(port)))
868 | if result == 0:
869 | openports.append(port)
870 | s.close()
871 | except Exception:
872 | pass
873 | self.lock.release()
874 | if len(openports) > 0:
875 | print("%s%s%s - %sFound open ports:%s %s%s%s" % (G, host, W, R, W, Y, ', '.join(openports), W))
876 |
877 | def run(self):
878 | self.lock = threading.BoundedSemaphore(value=20)
879 | for subdomain in self.subdomains:
880 | t = threading.Thread(target=self.port_scan, args=(subdomain, self.ports))
881 | t.start()
882 |
883 |
884 | def main(domain, threads, savefile, ports, silent, verbose, enable_bruteforce, engines):
885 | bruteforce_list = set()
886 | search_list = set()
887 |
888 | if is_windows:
889 | subdomains_queue = list()
890 | else:
891 | subdomains_queue = multiprocessing.Manager().list()
892 |
893 | # Check Bruteforce Status
894 | if enable_bruteforce or enable_bruteforce is None:
895 | enable_bruteforce = True
896 |
897 | # Validate domain
898 | domain_check = re.compile("^(http|https)?[a-zA-Z0-9]+([\-\.]{1}[a-zA-Z0-9]+)*\.[a-zA-Z]{2,}$")
899 | if not domain_check.match(domain):
900 | if not silent:
901 | print(R + "Error: Please enter a valid domain" + W)
902 | return []
903 |
904 | if not domain.startswith('http://') or not domain.startswith('https://'):
905 | domain = 'http://' + domain
906 |
907 | parsed_domain = urlparse.urlparse(domain)
908 |
909 | if not silent:
910 | print(B + "[-] Enumerating subdomains now for %s" % parsed_domain.netloc + W)
911 |
912 | if verbose and not silent:
913 | print(Y + "[-] verbosity is enabled, will show the subdomains results in realtime" + W)
914 |
915 | supported_engines = {'baidu': BaiduEnum,
916 | 'yahoo': YahooEnum,
917 | 'google': GoogleEnum,
918 | 'bing': BingEnum,
919 | 'ask': AskEnum,
920 | 'netcraft': NetcraftEnum,
921 | 'dnsdumpster': DNSdumpster,
922 | 'virustotal': Virustotal,
923 | 'threatcrowd': ThreatCrowd,
924 | 'ssl': CrtSearch,
925 | 'passivedns': PassiveDNS
926 | }
927 |
928 | chosenEnums = []
929 |
930 | if engines is None:
931 | chosenEnums = [
932 | BaiduEnum, YahooEnum, GoogleEnum, BingEnum, AskEnum,
933 | NetcraftEnum, DNSdumpster, Virustotal, ThreatCrowd,
934 | CrtSearch, PassiveDNS
935 | ]
936 | else:
937 | engines = engines.split(',')
938 | for engine in engines:
939 | if engine.lower() in supported_engines:
940 | chosenEnums.append(supported_engines[engine.lower()])
941 |
942 | # Start the engines enumeration
943 | enums = [enum(domain, [], q=subdomains_queue, silent=silent, verbose=verbose) for enum in chosenEnums]
944 | for enum in enums:
945 | enum.start()
946 | for enum in enums:
947 | enum.join()
948 |
949 | subdomains = set(subdomains_queue)
950 | for subdomain in subdomains:
951 | search_list.add(subdomain)
952 |
953 | if enable_bruteforce:
954 | if not silent:
955 | print(G + "[-] Starting bruteforce module now using subbrute.." + W)
956 | record_type = False
957 | path_to_file = os.path.dirname(os.path.realpath(__file__))
958 | subs = os.path.join(path_to_file, 'subbrute', 'names.txt')
959 | resolvers = os.path.join(path_to_file, 'subbrute', 'resolvers.txt')
960 | process_count = threads
961 | output = False
962 | json_output = False
963 | bruteforce_list = subbrute.print_target(parsed_domain.netloc, record_type, subs, resolvers, process_count, output, json_output, search_list, verbose)
964 |
965 | subdomains = search_list.union(bruteforce_list)
966 |
967 | if subdomains:
968 | subdomains = sorted(subdomains, key=subdomain_sorting_key)
969 |
970 | if savefile:
971 | write_file(savefile, subdomains)
972 |
973 | if not silent:
974 | print(Y + "[-] Total Unique Subdomains Found: %s" % len(subdomains) + W)
975 |
976 | if ports:
977 | if not silent:
978 | print(G + "[-] Start port scan now for the following ports: %s%s" % (Y, ports) + W)
979 | ports = ports.split(',')
980 | pscan = portscan(subdomains, ports)
981 | pscan.run()
982 |
983 | elif not silent:
984 | for subdomain in subdomains:
985 | print(G + subdomain + W)
986 | return subdomains
987 |
988 |
989 | def interactive():
990 | args = parse_args()
991 | domain = args.domain
992 | threads = args.threads
993 | savefile = args.output
994 | ports = args.ports
995 | enable_bruteforce = args.bruteforce
996 | verbose = args.verbose
997 | engines = args.engines
998 | if verbose or verbose is None:
999 | verbose = True
1000 | if args.no_color:
1001 | no_color()
1002 | banner()
1003 | res = main(domain, threads, savefile, ports, silent=False, verbose=verbose, enable_bruteforce=enable_bruteforce, engines=engines)
1004 |
1005 | if __name__ == "__main__":
1006 | interactive()
1007 |
--------------------------------------------------------------------------------