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() # 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 findInLink(self, url, linkId, totalLinks, hostname): # Uso del crawler crawler = WCrawler() # El resultado es de tipo HTML result = None try: result = crawler.httpRequest(url=url) # 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): # Nothing return [] # 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 [] # Procesa cada nombre de dominio links = [] for item in matches: it = item.decode() # Agrega el subdominio encontrado a la pila local links.append(it) return links
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 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 paginate(self, pageNumber=1): # Contexto de la búsqueda de la página actual searchContext = { 'max-pages': 15, 'max-result': 10, 'start-index': 1, 'query': 'domain:' + self.context.baseHostname } # ¿Hay resultados del método actual? if (self.hostnames): # Excluye los subdominios ya conocidos searchContext['query'] += (' -domain:' + ' -domain:'.join(self.hostnames)) # Número del resultado de inicio actual searchContext['start-index'] = (( (pageNumber - 1) * searchContext['max-result']) + 1) # Header message for pagination self.context.out( message=self.context.strings['methods']['bing']['pagination']) # Uso del crawler crawler = WCrawler() # El resultado es de tipo json result = None try: result = crawler.httpRequest( 'https://www.bing.com/search?' + '&q=' + crawler.urlencode(searchContext['query']) + '&first=' + str(searchContext['start-index'])) # Libera la memoria (no es necesario un contexto de navegación) crawler.clearContext() except Exception as e: self.context.out( self.context.strings['methods']['bing']['no-connect']) return # ¿La respuesta HTTP es OK? if (result['status-code'] != 200): self.context.out(message=self.context.strings['methods']['bing'] ['wrong-status-http'], parseDict={'id': result['status-code']}) return # Busca cada nombre de dominio # Ejemplo de resultados: <cite>https://foo<strong>ejemplo.com</strong> matches = re.findall( br'>([\w\.\-\_\$]+?\.' + re.escape(self.context.baseHostname).encode() + br')', result['response-content'].replace( b'<strong>' + self.context.baseHostname.encode(), b'.' + self.context.baseHostname.encode())) if (len(matches) == 0): # No hay resultados self.context.out( self.context.strings['methods']['bing']['no-more-results']) return # Procesa cada nombre de dominio encontrado for item in matches: # ¿El resultado es un subdominio inválido? if (not item.decode().endswith('.' + self.context.baseHostname)): 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()) # Agrega el subdominio encontrado a la pila global de resultados self.context.addHostName( hostname=item.decode(), messageFormat=self.context.strings['methods']['bing'] ['item-found']) # ¿Hay mas páginas donde buscar? if (not b'sw_next' in result['response-content']): self.context.out( self.context.strings['methods']['bing']['no-more-results']) return # Límite de busqueda de páginas if (pageNumber >= searchContext['max-pages']): self.context.out( self.context.strings['methods']['bing']['no-more-results']) return # Continua con la siguiente página self.paginate(pageNumber=pageNumber + 1)
def find(self): # Mensaje de la cabecera del método self.context.out( message=self.context.strings['method-begin'], parseDict={ 'current': self.context.progress['methods']['current'], 'total': self.context.progress['methods']['total'], 'title': self.context.strings['methods']['certificate-details']['title'] }) # Uso del crawler crawler = WCrawler() # El resultado es de tipo json result = None try: result = crawler.httpRequest( url='https://certificatedetails.com/api/list/' + crawler.urlencode(self.context.baseHostname)) # Libera la memoria (no es necesario un contexto de navegación) crawler.clearContext() except Exception as e: self.context.out(self.context.strings['methods'] ['certificate-details']['no-connect']) return # ¿La respuesta HTTP es OK? if (result['status-code'] != 200): self.context.out(message=self.context.strings['methods'] ['certificate-details']['wrong-status-http'], parseDict={'id': result['status-code']}) return try: # Convierte el resultado en un objeto de tipo json result = json.loads(result['response-content']) except Exception as e: self.context.out(self.context.strings['methods'] ['certificate-details']['corrupt-response']) return if ((not isinstance(result, list)) or (len(result) == 0)): self.context.out(self.context.strings['methods'] ['certificate-details']['empty']) return # Procesa cada nombre de dominio encontrado for item in result: # ¿Es un subdominio válido? if (not item['CommonName'].endswith('.' + self.context.baseHostname)): continue # Evita los resultados duplicados utilizando la pila local if (item['CommonName'] in self.hostnames): continue # Agrega el subdominio encontrado a la pila local self.hostnames.append(item['CommonName']) # Agrega el subdominio encontrado a la pila global de resultados self.context.addHostName( hostname=item['CommonName'], messageFormat=self.context.strings['methods']['crt-sh'] ['item-found']) # Mensaje de cabecera del comienzo de la obtención de todos los enlaces self.context.out(self.context.strings['methods']['certificate-details'] ['find-links']) # 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. for item in result: linkId += 1 self.findInLink(url='https://certificatedetails.com' + item['Link'], linkId=linkId, totalLinks=len(result))
def findInLink(self, url, linkId, totalLinks): # Mensaje de cabereca de inicio de obtención del enlace self.context.out(message=self.context.strings['methods'] ['certificate-details']['find-link'], parseDict={ 'link-id': linkId, 'total-links': totalLinks }, end='') # Uso del crawler crawler = WCrawler() # El resultado es de tipo HTML result = None try: result = crawler.httpRequest(url=url) # Libera la memoria (no es necesario un contexto de navegación) crawler.clearContext() except Exception as e: self.context.out(self.context.strings['methods'] ['certificate-details']['find-clear'], end='') return self.context.out(self.context.strings['methods']['certificate-details'] ['find-clear'], end='') # ¿La respuesta HTTP es OK? if (result['status-code'] != 200): # Nothing return # Busca todos los posibles subdominios matches = re.findall( br'>([\w\.\-\_\$]+?\.' + re.escape(self.context.baseHostname).encode() + br')<', result['response-content']) # ¿Hay resultados? if (len(matches) == 0): return # Procesa cada nombre de dominio for item in matches: if ( # Evita los resultados duplicados utilizando la pila local (not item.decode() in self.hostnames) and # ¿Es un subdominio válido? (item.decode().endswith('.' + self.context.baseHostname))): # Agrega el subdominio encontrado a la pila local self.hostnames.append(item.decode()) # Agrega el subdominio encontrado a la pila global de resultados self.context.addHostName( hostname=item.decode(), messageFormat=self.context.strings['methods'] ['certificate-details']['item-found'])
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): # Mensaje de la cabecera del método self.context.out( message=self.context.strings['method-begin'], parseDict={ 'current': self.context.progress['methods']['current'], 'total': self.context.progress['methods']['total'], 'title': self.context.strings['methods']['dnsdumpster']['title'] }) # Paso 1: BçObtiene el Token XSRF y mantiene el contexto de navegación # para conservar la cookie de sesión. self.context.out(self.context.strings['methods']['dnsdumpster'] ['getting-token-xsrf']) # 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: self.context.out( self.context.strings['methods']['dnsdumpster']['no-connect']) return # ¿La respuesta HTTP es OK? if (result['status-code'] != 200): self.context.out(message=self.context.strings['methods'] ['dnsdumpster']['wrong-status-http'], parseDict={'id': result['status-code']}) return # Busca el token XSRF matches = re.search( br'name=["\']csrfmiddlewaretoken["\']\s+value=["\'](.+?)["\']', result['response-content'], re.I | re.M) if (not matches): # No se pudo encontrar el token self.context.out(self.context.strings['methods']['dnsdumpster'] ['no-xsrf-token-found']) return # Guarda el roken XSRF en la variable local para reutilizar la variable # 'matches'. tokenXsrf = matches.group(1) # Paso 2: Envía la solicitud HTTP con el subdominio a buscar self.context.out(self.context.strings['methods']['dnsdumpster'] ['getting-subdomains']) # El resultado es de tipo HTML result = None try: result = crawler.httpRequest(url='https://dnsdumpster.com/', postData={ 'csrfmiddlewaretoken': tokenXsrf, 'targetip': self.context.baseHostname }) except Exception as e: self.context.out( self.context.strings['methods']['dnsdumpster']['no-connect']) return # ¿La respuesta HTTP es OK? if (result['status-code'] != 200): print(result) self.context.out(message=self.context.strings['methods'] ['dnsdumpster']['wrong-status-http'], parseDict={'id': result['status-code']}) return # Busca todos los resultados matches = re.findall( br'>([\w\.\-\_\$]+?\.' + re.escape(self.context.baseHostname).encode() + br')<', result['response-content']) # ¿Hay resultados? if (len(matches) == 0): self.context.out( self.context.strings['methods']['dnsdumpster']['empty']) return # Procesa todos los subdominios encontrados for item in matches: # Agrega el subdominio encontrado a la pila global de resultados self.context.addHostName( hostname=item.decode(), messageFormat=self.context.strings['methods']['dnsdumpster'] ['item-found'])
def find(self): # Mensaje de la cabecera del método self.context.out(message=self.context.strings['method-begin'], parseDict={ 'current': self.context.progress['methods']['current'], 'total': self.context.progress['methods']['total'], 'title': self.context.strings['methods']['robtex']['title'] }) # Uso del crawler crawler = WCrawler() crawler.defaultTimeout = 30 # Resultado de tipo HTML result = None try: result = crawler.httpRequest( url='https://www.robtex.com/dns-lookup/' + crawler.urlencode(self.context.baseHostname)) # Libera la memoria (no es necesario un contexto de navegación) crawler.clearContext() except Exception as e: # Imposible navegar self.context.out( self.context.strings['methods']['robtex']['no-connect']) return # ¿La respuesta HTTP es OK? if (result['status-code'] != 200): self.context.out(message=self.context.strings['methods']['robtex'] ['wrong-status-http'], parseDict={'id': result['status-code']}) 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(self.context.baseHostname).encode() + br')<', result['response-content']) # ¿Hay resultados? if (len(matches) == 0): self.context.out( self.context.strings['methods']['robtex']['empty']) return # Procesa todos los resultados for item in matches: # Agrega el subdominio encontrado a la pila global de resultados self.context.addHostName( hostname=item.decode(), messageFormat=self.context.strings['methods']['robtex'] ['item-found'])
def paginate(self, pageNumber=1): # Contexto de la búsqueda de la página actual searchContext = { 'max-pages': 15, 'max-result': 10, 'start-index': 1, 'query': 'site:' + self.context.baseHostname } # ¿Hay resultados del método actual? if (self.hostnames): # Excluye los subdominios ya conocidos searchContext['query'] += ' -site:' + ' -site:'.join( self.hostnames) # Número del resultado de inicio actual searchContext['start-index'] = (( (pageNumber - 1) * searchContext['max-result']) + 1) # Mensaje inicial de la paginación self.context.out( self.context.strings['methods']['google']['pagination']) # Uso del crawler crawler = WCrawler() # El resultado es de tipo json result = None try: # Navega result = crawler.httpRequest( 'https://www.googleapis.com/customsearch/v1?' + 'cx=' + crawler.urlencode(self.googleCx) + '&key=' + crawler.urlencode(self.googleApiKey) + '&q=' + crawler.urlencode(searchContext['query']) + '&start=' + str(searchContext['start-index']) + '&filter=1&safe=off&num=' + str(searchContext['max-result'])) # Libera la memoria (no es necesario un contexto de navegación) crawler.clearContext() except Exception as e: # Imposible navegar self.context.out( self.context.strings['methods']['google']['no-connect']) return # 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]): self.context.out( self.context.strings['methods']['google']['no-more-results']) return # ¿La respuesta HTTP es OK? if (result['status-code'] != 200): self.context.out(message=self.context.strings['methods']['google'] ['wrong-status-http'], parseDict={'id': result['status-code']}) return try: # Convierte el resultado en un objeto de tipo json result = json.loads(result['response-content']) except Exception as e: # Contenido corrupto, no es de tipo json procesable self.context.out( self.context.strings['methods']['google']['corrupt-response']) return # ¿Hay resultados procesables? if ((not 'items' in result) or (len(result['items']) == 0)): # No hay más resultados self.context.out( self.context.strings['methods']['google']['no-more-results']) return # Procesa cada resultado for item in result['items']: # ¿El resultado es un subdominio inválido? if (not item['displayLink'].endswith('.' + self.context.baseHostname)): continue # Evita los resultados duplicados utilizando la pila local if (item['displayLink'] in self.hostnames): continue # Agrega el subdominio encontrado a la pila local self.hostnames.append(item['displayLink']) # Agrega el subdominio encontrado a la pila global de resultados self.context.addHostName( hostname=item['displayLink'], messageFormat=self.context.strings['methods']['google'] ['item-found']) # Retorna a la primera página nuevamente debido a que la búsqueda # debe contener la exclusión del subdominio encontrado, por ejemplo: # site: example.com -site:foo.example.com pageNumber = 0 # Límite de busqueda de páginas if (pageNumber >= searchContext['max-pages']): self.context.out( self.context.strings['methods']['google']['no-more-results']) return # Continua con la siguiente página self.paginate(pageNumber=pageNumber + 1)
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
def threadCheck(self): while (True): if (len(self.hostnameContext['check-ports']) == 0): # No hay mas puertos donde buscar break # Obtiene el siguente puerto a buscar de la pila local port = int(self.hostnameContext['check-ports'].pop()) for protocol in ['http', 'https']: # Omite conflictos if (((protocol == 'http') and (port == 443)) or ((protocol == 'https') and (port == 80))): continue # Compone la dirección URL final url = protocol + '://' + self.hostnameContext[ 'current-hostname'] + '/' if (((protocol == 'http') and (port != 80)) or ((protocol == 'https') and (port != 443))): url = protocol + '://' + self.hostnameContext[ 'current-hostname'] + ':' + str(port) + '/' self.context.out(message=( self.context.strings['filters']['http']['progress-clear'] + self.context.strings['filters']['http']['progress']), parseDict={'url': url}, end='') # 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] self.context.out(message=( self.context.strings['filters']['http']['progress-clear'] + self.context.strings['filters']['http']['found'] + '\n' + self.context.strings['filters']['http']['progress-wait']), parseDict={ 'url': url, 'title': (title if title else ''), 'status-code': (result['status-code'] if result['status-code'] else '-'), 'status-message': (result['status-message'].decode() if result['status-message'] else '-') }, end='') # ¿Existe una estructura definida para el nombre de dominio? if (self.context.results['ip-address']['items'][ self.hostnameContext['current-ip-address']]['items'] ['hostnames']['items'][ self.hostnameContext['current-hostname']] is None): # Crea la estructura de resultados self.context.results['ip-address']['items'][ self.hostnameContext['current-ip-address']]['items'][ 'hostnames']['items'][ self.hostnameContext['current-hostname']] = { 'title': self.hostnameContext['current-hostname'], 'items': { 'http-services': { 'title': self.context.strings['filters'] ['http']['node-tree'] ['http-title'], 'items': {} } } } # ¿La estructura de servicios HTTP existe para el nombre de dominio actual? if (not 'http-services' in self.context.results['ip-address'] ['items'][self.hostnameContext['current-ip-address']] ['items']['hostnames']['items'][self.hostnameContext[ 'current-hostname']]['items'].keys()): # Añade la estructura de resultados a una ya existente self.context.results['ip-address']['items'][ self.hostnameContext['current-ip-address']]['items'][ 'hostnames']['items'][ self.hostnameContext['current-hostname']][ 'items']['http-services'] = { 'title': self.context.strings['filters']['http'] ['node-tree']['http-title'], 'items': {} } # Mensaje final con el resultado nodeLine = self.context.parseString( message=self.context.strings['filters']['http'] ['node-tree']['http-service'], parseDict={ 'url': url, 'title': (title if title else ''), 'status-code': (result['status-code'] if result['status-code'] else '-'), 'status-message': (result['status-message'].decode() if result['status-message'] else '-') }) # Agrega a lapila de resultados si no existe (como objeto) if (not nodeLine in self.context.results['ip-address']['items'] [self.hostnameContext['current-ip-address']]['items'] ['hostnames']['items'][ self.hostnameContext['current-hostname']]['items'] ['http-services']['items'].keys()): self.context.results['ip-address']['items'][ self.hostnameContext['current-ip-address']]['items'][ 'hostnames']['items'][self.hostnameContext[ 'current-hostname']]['items']['http-services'][ 'items'][nodeLine] = None
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 findInApi(self, nextUrl=None, pageId=1): # Mensaje de la paginación self.context.out( message=self.context.strings['methods']['virus-total']['paginating'], parseDict={ 'number': pageId } ) # Uso del crawler crawler = WCrawler() # El resultado es de tipo json result = None try: if(nextUrl is None): result = crawler.httpRequest( url='https://www.virustotal.com/ui/domains/' + crawler.urlencode(self.context.baseHostname) + '/subdomains?limit=40' ) else: result = crawler.httpRequest(nextUrl) # Libera la memoria (no es necesario un contexto de navegación) crawler.clearContext() except Exception as e: # Imposible navegar self.context.out( self.context.strings['methods']['virus-total']['no-connect'] ) return # ¿La respuesta HTTP es OK? if(result['status-code'] != 200): self.context.out( message=self.context.strings['methods']['virus-total']['wrong-status-http'], parseDict={ 'id': result['status-code'] } ) return try: # Convierte el resultado en un objeto de tipo json result = json.loads(result['response-content']) except Exception as e: # Contenido corrupto, no es de tipo json procesable self.context.out( self.context.strings['methods']['virus-total']['corrupt-response'] ) return # ¿Hay contenido de la respuesta HTTP? if(len(result['data']) == 0): # No hay contenido, por lo cual tampoco hay más resultados self.context.out(self.context.strings['methods']['virus-total']['no-more']) 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'])) # Agrega el subdominio encontrado a la pila global de resultados self.context.addHostName( hostname=str(item['id']), messageFormat=self.context.strings['methods']['virus-total']['item-found'] ) # ¿Necesita continuar paginando resultados? if( ('links' in result) and ('next' in result['links']) and (result['links']) ): # Continua con la siguiente página self.findInApi( nextUrl=str(result['links']['next']), pageId=(pageId + 1) )
def find(self): # Mensaje de la cabecera del método self.context.out(message=self.context.strings['method-begin'], parseDict={ 'current': self.context.progress['methods']['current'], 'total': self.context.progress['methods']['total'], 'title': self.context.strings['methods']['crt-sh']['title'] }) # Uso del crawler crawler = WCrawler() crawler.defaultTimeout = 60 # El resultado es de tipo json result = None try: result = crawler.httpRequest( url='https://crt.sh/?q=' + crawler.urlencode('%.' + self.context.baseHostname) + '&output=json') # Libera la memoria (no es necesario un contexto de navegación) crawler.clearContext() except Exception as e: self.context.out( self.context.strings['methods']['crt-sh']['no-connect']) return # ¿La respuesta HTTP es OK? if (result['status-code'] != 200): self.context.out(message=self.context.strings['methods']['crt-sh'] ['wrong-status-http'], parseDict={'id': result['status-code']}) return try: # Convierte el resultado en un objeto de tipo json result = json.loads(result['response-content']) except Exception as e: self.context.out( self.context.strings['methods']['crt-sh']['corrupt-response']) return if ((not isinstance(result, list)) or (len(result) == 0)): self.context.out( self.context.strings['methods']['crt-sh']['empty']) return # Procesa cada nombre de dominio encontrado for item in result: # Evita los resultados duplicados utilizando la pila local if (item['name_value'] in self.hostnames): continue # Agrega el subdominio encontrado a la pila local self.hostnames.append(item['name_value']) # Agrega el subdominio encontrado a la pila global de resultados self.context.addHostName( hostname=item['name_value'], messageFormat=self.context.strings['methods']['crt-sh'] ['item-found'])