def findHttpServices(self, address): h = Helper() if ((h.ip_validate(address) is None) and (h.hostname_validate(address) is None)): return {} th = [] for tn in range(1, 100): t = threading.Thread(target=self.check, kwargs={'address': address}) t.setDaemon(True) th.append(t) for t in th: t.start() t.join() result = { 'hostname': address, 'addresses': h.resolve_dns(address), 'webservices': self.webservices } return result
def run_method(langbuf, param): h = Helper() Logging.log(h.formatter(h.getlang(langbuf, 'running', None), [hostname]), LogLevel.CLI) print(' ') if param is not None: for p in param: if (p == '0'): render_tree(request_method(MethodAxfr(), 'AXFR')) elif (p == '1'): render_tree(request_method(MethodDnsQueries(), 'DNS Queries')) elif (p == '2'): render_tree(request_method(MethodVirusTotal(), 'VirusTotal')) elif (p == '3'): render_tree(request_method(MethodRobtex(), 'Robtex')) elif (p == '4'): render_tree(request_method(MethodCrtSh(), 'CRTSH')) elif (p == '5'): render_tree( request_method(MethodCertificateDetails(), 'CertificateDetails')) elif (p == '6'): render_tree(request_method(MethodGoogle(), 'Google CSE')) elif (p == '7'): render_tree(request_method(MethodBing(), 'Bing')) elif (p == '8'): render_tree(request_method(MethodDnsDumpster(), 'DNS Dumpster'))
def filter(self, address): h = Helper() # Direcciones IP que se está buscando actualmente, para recibir desde el # sniffer. self.remoteIpAddressStack = h.resolve_dns(address) # Corre el sniffer en busca de los paquetes de respuesta de puertos threadHandler = threading.Thread(target=self.sniffer) # Previene la impresión de mensajes de error al final del hilo # principal cuando se cancela el progreso con Conrol+C. threadHandler.setDaemon(True) # Ejecuta el hilo de proceso threadHandler.start() for ip in self.remoteIpAddressStack: # Realiza la búsqueda de puertos self.findPorts(address) # Iteración cada x segundos time.sleep(10) self.canContinue = False self.socketHandlerBind.close() # Espera a que finalice el hilo de proceso del sniffer. threadHandler.join() return self.result
def findPorts(self, address): h = Helper() # Omite los rangos locales # TODO: Puede ser requerido para pentesting. if h.ip_validate(address) is not None: if (IP(address).iptype() in ['PRIVATE', 'LOOPBACK']): return interface = Interface() tcpHelper = TCPHelper() # Dirección IP local donde llegarán los paquetes localIpAddress = interface.getSourceAddress() # Rango de puertos a revisar for port in range(1, 65535): # Envía un paquete syn a modo de señuelo mientras que el hilo de # proceso del socket a escucha está listo para recibir las # respuestas. try: tcpHelper.sendSyn(sourceIp=localIpAddress, toAddress=address, dstPort=port) except Exception as e: # Ok, puede suceder, es normal. # Por alguna extraña razón el socket en cierto punto arroja un # acceso denegado indicando que no tengo permisos para la # operación a pesar de tener privilegios elevados, pero de todas # maneras el paquete se envía sin problemas. pass
def define_host(langbuf, param): global hostname h = Helper() if h.hostname_validate(param) is None: Logging.log(h.getlang(langbuf, 'errors', 'invalid-hostname'), LogLevel.DANGER) exit(-1) else: hostname = param
def find(self, hostname): h = Helper() # Pila local de subdominios encontrados que evita al duplicidad hostnames = [] # Procesa cada tipo de registro DNS for recordType in ['MX', 'TXT', 'SPF', 'NS']: # ¿Hubo respuesta? answer = None try: answer = dns.resolver.query( hostname, recordType, tcp=True # Mejora el resultado, evita la pérdida de paquetes ) except Exception as e: # No fue posible realizar la consulta DNS continue for rdata in answer: # Registro en texto plano y en bruto plainRecord = rdata.to_text().strip('"') # Busca todos los posibles subdominios en la respuesta plana matches = re.findall( r'([a-zA-Z0-9\.\-\_\$]+?\.' + re.escape(hostname) + r')', plainRecord) # ¿Hay resultados? if (len(matches) == 0): continue # Procesa cada subdominio encontrado for item in matches: if ( # Es un subdominio válido? (not item.endswith(h.formatter('.{}', [hostname]))) or # ¿El subdominio existe en la pila local? (evita la # duplicidad de resultados). (item in hostnames)): continue # Agrega el subdominio encontrado a la pila local hostnames.append({ 'subdomain': item, 'recordType': recordType }) # ¿Hubo resultados finalmente? if len(hostnames): return hostnames else: return []
def check(self, address): self.webservices = [] while (len(self.stack)): port = self.stack.pop() for protocol in ['http', 'https']: # Omite conflictos if (((protocol == 'http') and (port == 443)) or ((protocol == 'https') and (port == 80))): continue h = Helper() if (((protocol == 'http') and (port == 80)) or ((protocol == 'https') and (port == 443))): continue url = h.formatter( '{}://{}:{}/', [protocol, address, str(port)]) # Uso del crawler crawler = WCrawler() result = None try: result = crawler.httpRequest(url) crawler.clearContext() except Exception as e: pass if ( # No fue posible conectar con el servidor (result is None) or # Puerto abierto pero no es un servicio HTTP (result['status-code'] == 0)): continue # Obtiene el contenido de la etiqueta HTML <title> matches = re.search(br'<title>(.+?)<\/title>', result['response-content'], re.I | re.M) title = None if (matches): title = str(matches.group(1))[2:][:-1] sm = result.get('status-message', '-').decode() sc = result.get('status-code') t = title if title else '' self.webservices.append({ 'url': url, 'title': t, 'status-code': sc, 'status-message': sm })
def find(self, hostname, url=None): h = Helper() # Uso del crawler crawler = WCrawler() # El resultado es de tipo json result = None if url is None: req = h.formatter( 'https://www.virustotal.com/ui/domains/{}/subdomains?limit=40', [hostname]) else: req = url try: result = crawler.httpRequest(req) # Libera la memoria (no es necesario un contexto de navegación) crawler.clearContext() except Exception as e: return [] # ¿La respuesta HTTP es OK? if (result['status-code'] != 200): return [] try: # Convierte el resultado en un objeto de tipo json result = json.loads(result['response-content']) except Exception as e: return [] # ¿Hay contenido de la respuesta HTTP? if (len(result['data']) == 0): return [] # Procesa todos los subdominios encontrados en la página actual for item in result['data']: # Evita los resultados duplicados utilizando la pila local if (str(item['id']) in self.hostnames): continue # Agrega el subdominio encontrado a la pila local self.hostnames.append(str(item['id'])) # ¿Necesita continuar paginando resultados? if (('links' in result) and ('next' in result['links']) and (result['links'])): self.find(hostname=hostname, url=str(result['links']['next'])) return self.hostnames
def find(self, hostname): h = Helper() nameServers = [] try: ans = dns.resolver.query(hostname, 'NS', tcp=True) nameServers = [a.to_text().strip('.') for a in ans] nameServers = set(nameServers) # Valores únicos nameServers = sorted(nameServers) # Valores ordenados except Exception as e: pass except dns.exception.DNSException as e: pass if (len(nameServers) == 0): return {} # Procesa cada registro for ns in nameServers: self.ns.append(ns) axfr = None try: # Crea la consulta AXFR axfr = dns.query.xfr(where=ns, zone=hostname, lifetime=5.0) except Exception as e: continue zone = None try: # Intenta obtener los resultados de la consulta zone = dns.zone.from_xfr(axfr) except Exception as e: continue if zone is None: continue # Procesa cada resultado for name, node in zone.nodes.items(): rdatasets = node.rdatasets for rdataset in rdatasets: if (str(name) == '@'): continue r = h.formatter('{}.{}', [str(name), hostname]) self.subdomains.append(r) return {'nameservers': self.ns, 'domains': self.subdomains}
def render_dict(d, tree): h = Helper() t = '' for o in d: m = d.get(o) if isinstance(m, list): t += h.formatter('{}: {} ', [o, ', '.join(m)]) elif isinstance(m, dict): for p in m: s = m.get(p) t += h.formatter('{}: {} ', [p, s]) else: t += h.formatter('{}: {} ', [o, m]) q = Node(t, parent=tree)
def find(self, hostname): h = Helper() # Uso del crawler crawler = WCrawler() # El resultado es de tipo json result = None mp = 15 q = h.formatter('domain:{}', [hostname]) for p in range(1, 16): req = h.formatter('https://www.bing.com/search?q={}&first={}', [q, p]) try: result = crawler.httpRequest(req) # Libera la memoria (no es necesario un contexto de navegación) crawler.clearContext() except Exception as e: return [] # ¿La respuesta HTTP es OK? if (result['status-code'] != 200): return [] # Busca cada nombre de dominio # Ejemplo de resultados: <cite>https://foo<strong>ejemplo.com</strong> matches = re.findall( br'>([\w\.\-\_\$]+?\.' + re.escape(hostname).encode() + br')', result['response-content'].replace( b'<strong>' + hostname.encode(), b'.' + hostname.encode())) if (len(matches) == 0): return [] # Procesa cada nombre de dominio encontrado for item in matches: # ¿El resultado es un subdominio inválido? if (not item.decode().endswith('.' + hostname)): continue # Evita los resultados duplicados utilizando la pila local if (item.decode() in self.hostnames): continue # Agrega el subdominio encontrado a la pila local self.hostnames.append(item.decode()) return self.hostnames
def parse_args(self, args): h = Helper() for flag in args.keys(): cmd = self.found_command(flag) if not cmd is None: runtime = cmd.get('runtime') runtime(self.langbuf, args[flag])
def run_server(langbuf, param): h = Helper() c = param.split(':') ip = '127.0.0.1' port = 3000 debug = 'on' if len(c): for v in c: if h.ip_validate(v) is not None: ip = v elif h.port_validate(v) is not None: port = v else: if v in ['on', 'off']: debug = v http = HttpServer(ip, port, debug) http.start()
def __init__(self): #Helper instance h = Helper() #Print banner Logging.log(h.ftext('WHK Subdomains Scanner', 'speed'), LogLevel.CLI) #Parse arguments from cli args = self.build_args() argc = len(args) #Check if arguments was passed if not argc > 0: Logging.log( '- No CLI arguments found, try --help command for more info', LogLevel.DANGER) exit(-1) self.load_lang(self.define_lang(args)) self.parse_args(args)
def find(self, hostname): h = Helper() # Uso del crawler crawler = WCrawler() crawler.defaultTimeout = 60 # El resultado es de tipo json result = None req = h.formatter('https://crt.sh/?q=%.{}&output=json', [hostname]) try: result = crawler.httpRequest(req) # Libera la memoria (no es necesario un contexto de navegación) crawler.clearContext() except Exception as e: return [] # ¿La respuesta HTTP es OK? if (result['status-code'] != 200): return [] try: # Convierte el resultado en un objeto de tipo json result = json.loads(result['response-content']) except Exception as e: return [] if ((not isinstance(result, list)) or (len(result) == 0)): return [] hostnames = [] # Procesa cada nombre de dominio encontrado for item in result: # Evita los resultados duplicados utilizando la pila local if (item['name_value'] in hostnames): continue # Agrega el subdominio encontrado a la pila local hostnames.append(item['name_value']) return hostnames
def findPorts(self, ipaddress): h = Helper() # Omite los rangos locales # TODO: Puede ser requerido para pentesting. if h.ip_validate(ipaddress) is not None: if (IP(ipaddress).iptype() in ['PRIVATE', 'LOOPBACK']): return {'ports': []} self.ips = h.resolve_dns(ipaddress) response = [] for ip in self.ips: if ip == ipaddress: continue result = {'record': {'ip': []}} self.stack = list(reversed(range(1, 65535))) result['record']['ip'] = ip result['record']['ports'] = [] # Punteros de los hilos de proceso threadsHandlers = [] # Linux por defecto soporta 1024 threads a menos que se modifique # los límites en /etc/security/limits.conf # 500 hilos por defecto for threadNumber in range(1, 500): # Puntero del hilo de proceso threadHandler = threading.Thread(target=self.threadCheck, kwargs={'ipaddress': ip}) # Previene la impresión de mensajes de error al final del hilo # principal cuando se cancela el progreso con Conrol+C. threadHandler.setDaemon(True) # Obtiene el identificador único del hilo de proceso threadsHandlers.append(threadHandler) # Ejecuta el hilo de proceso threadHandler.start() for threadHandler in threadsHandlers: # Espera a que finalice el hilo de proceso threadHandler.join() result['record']['ports'] = sorted(self.ports) response.append(result) self.ports = [] return response
def define_lang(self, args): h = Helper() #Trying found by long flag lang = args.get('lang') if lang is None: #Trying found by short flag lang = args.get('l') if lang is None: langpath = h.formatter('resources/lang/{}.json', [get_locale()]) return langpath else: langpath = h.formatter('resources/lang/{}.json', [lang.lower()]) return langpath else: langpath = h.formatter('resources/lang/{}.json', [lang.lower()]) return langpath
def find(self, hostname): h = Helper() # Uso del crawler crawler = WCrawler() crawler.defaultTimeout = 30 # Resultado de tipo HTML result = None req = h.formatter('https://www.robtex.com/dns-lookup/{}', [hostname]) try: result = crawler.httpRequest(req) # Libera la memoria (no es necesario un contexto de navegación) crawler.clearContext() except Exception as e: return [] # ¿La respuesta HTTP es OK? if (result['status-code'] != 200): return [] # Elimina las etiquetas de negritas del resultado: foo.<b>domain.com</b> result['response-content'] = result['response-content'].replace( b'<b>', b'').replace(b'</b>', b'') # Busca todos los posibles subdominios matches = re.findall( br'>([\w\.\-\_\$]+?\.' + re.escape(hostname).encode() + br')<', result['response-content']) # ¿Hay resultados? if (len(matches) == 0): return [] result = [] # Procesa todos los resultados for item in matches: result.append(item.decode()) return result
def run_filter(langbuf, param): h = Helper() if param is not None: res = [] for p in param: if (p == '0'): if ((os.geteuid() == 0) or (os.getenv('SUDO_USER') is not None)): f = FilterRawPorts() render_tree(request_filter(f.filter(hostname), 'Raw Ports')) else: Logging.log(h.getlang(langbuf, 'errors', 'root-required'), LogLevel.DANGER) elif (p == '1'): f = FilterPorts() render_tree(request_filter(f.findPorts(hostname), 'Ports')) elif (p == '2'): f = FilterHttpServices() render_tree( request_filter(f.findHttpServices(hostname), 'Http Services'))
def load_lang(self, langpath): h = Helper() if not h.langexists(langpath): Logging.log('Can\'t define language to show, exiting...', LogLevel.DANGER) exit(-1) else: self.langbuf = lang_from_path(langpath) if not isinstance(self.langbuf, dict): Logging.log('- Error loading language file', LogLevel.DANGER) exit(-1) Logging.log( h.formatter(h.getlang(self.langbuf, 'header', None), [h.version(), h.gettime()]), LogLevel.CLI)
def find(self, hostname): h = Helper() # ¿La llave de la API de Google existe? if (not self.googleApiKey.strip()): return [] q = h.formatter('site:{}', [hostname]) hostnames = [] for i in range(1, 16): # ¿Hay resultados del método actual? if (hostnames): # Excluye los subdominios ya conocidos q += h.formatter('-site:{}', [' -site:'.join(hostnames)]) # Uso del crawler crawler = WCrawler() # El resultado es de tipo json result = None req = h.formatter( 'https://www.googleapis.com/customsearch/v1?cx={}&key={}&q={}&start={}&filter=1&safe=off&num={}', [self.googleCx, self.googleApiKey, q, i, 10]) try: # Navega result = crawler.httpRequest(req) # Libera la memoria (no es necesario un contexto de navegación) crawler.clearContext() except Exception as e: break # Los estados 403 y 400 indican que no hay más resultados o que la API # está saturada con solicitudes. if (result['status-code'] in [403, 400]): break # ¿La respuesta HTTP es OK? if (result['status-code'] != 200): break try: # Convierte el resultado en un objeto de tipo json result = json.loads(result['response-content']) except Exception as e: break # ¿Hay resultados procesables? if ((not 'items' in result) or (len(result['items']) == 0)): break # Procesa cada resultado for item in result['items']: f = h.formatter('.{}', [hostname]) # ¿El resultado es un subdominio inválido? if (not item['displayLink'].endswith(f)): continue # Evita los resultados duplicados utilizando la pila local if (item['displayLink'] in hostnames): continue # Agrega el subdominio encontrado a la pila local hostnames.append(item['displayLink']) return hostnames
def render_tree(tree): h = Helper() for pre, fill, node in RenderTree(tree): Logging.log(h.formatter("{}{}", [pre, node.name]), LogLevel.CLI)
def show_help(langbuf, param): h = Helper() Logging.log(h.getlang(langbuf, 'usage', None), LogLevel.CLI)
def dashboard(): h = Helper() return render_template('dashboard.html', version=h.version())
def find(self, hostname): h = Helper() # Uso del crawler, por defecto guardará la cookie de sesión manteniendo # el contexto del flujo de la navegación. crawler = WCrawler() # El resultado es de tipo HTML result = None try: result = crawler.httpRequest(url='https://dnsdumpster.com/') except Exception as e: return [] # ¿La respuesta HTTP es OK? if (result['status-code'] != 200): return [] # Busca el token XSRF matches = re.search( br'name=["\']csrfmiddlewaretoken["\']\s+value=["\'](.+?)["\']', result['response-content'], re.I | re.M) if (not matches): return [] # Guarda el roken XSRF en la variable local para reutilizar la variable # 'matches'. tokenXsrf = matches.group(1) # El resultado es de tipo HTML result = None try: result = crawler.httpRequest(url='https://dnsdumpster.com/', postData={ 'csrfmiddlewaretoken': tokenXsrf, 'targetip': hostname }) except Exception as e: return [] # ¿La respuesta HTTP es OK? if (result['status-code'] != 200): return [] # Busca todos los resultados matches = re.findall( br'>([\w\.\-\_\$]+?\.' + re.escape(hostname).encode() + br')<', result['response-content']) # ¿Hay resultados? if (len(matches) == 0): return [] hostnames = [] # Procesa todos los subdominios encontrados for item in matches: hostnames.append(item.decode()) return hostnames
def find(self, hostname): h = Helper() # Uso del crawler crawler = WCrawler() # El resultado es de tipo json result = None req = h.formatter('https://certificatedetails.com/api/list/{}', [hostname]) try: result = crawler.httpRequest(req) # Libera la memoria (no es necesario un contexto de navegación) crawler.clearContext() except Exception as e: return [] # ¿La respuesta HTTP es OK? if (result['status-code'] != 200): return [] try: # Convierte el resultado en un objeto de tipo json result = json.loads(result['response-content']) except Exception as e: return [] if ((not isinstance(result, list)) or (len(result) == 0)): return [] # Procesa cada nombre de dominio encontrado items = [] for item in result: subdomain = h.formatter('.{}', [hostname]) # ¿Es un subdominio válido? if (not item['CommonName'].endswith(subdomain)): continue # Evita los resultados duplicados utilizando la pila local if (item['CommonName'] in items): continue # Agrega el subdominio encontrado a la pila local items.append(item['CommonName']) self.hostnames.append({'subdomains': list(set(items))}) # Identificador actual del enlace linkId = 0 # Procesa cada enlace # Precaución: Un mismo nombre de dominio repetido puede contener uno o # más certificados diferentes. linkres = [] for item in result: linkId += 1 req = h.formatter('https://certificatedetails.com{}', [item['Link']]) linkres += self.findInLink(url=req, linkId=linkId, totalLinks=len(result), hostname=hostname) self.hostnames.append({'links': list(set(linkres))}) return self.hostnames