mirror of
https://github.com/laramies/theHarvester.git
synced 2024-11-11 18:03:10 +08:00
973220d3d1
Added Port scanning, Subdomain takeover, Fixed Shodan, improved Dns bruteforce and added SQLite support to store the results.
167 lines
4.7 KiB
Python
167 lines
4.7 KiB
Python
import gzip
|
|
import requests
|
|
import json
|
|
|
|
from .exception import APIError
|
|
|
|
try:
|
|
basestring
|
|
except NameError:
|
|
basestring = str
|
|
|
|
|
|
def create_facet_string(facets):
|
|
"""Converts a Python list of facets into a comma-separated string that can be understood by
|
|
the Shodan API.
|
|
"""
|
|
facet_str = ''
|
|
for facet in facets:
|
|
if isinstance(facet, basestring):
|
|
facet_str += facet
|
|
else:
|
|
facet_str += '%s:%s' % (facet[0], facet[1])
|
|
facet_str += ','
|
|
return facet_str[:-1]
|
|
|
|
|
|
def api_request(key, function, params=None, data=None, base_url='https://api.shodan.io', method='get', retries=1):
|
|
"""General-purpose function to create web requests to SHODAN.
|
|
|
|
Arguments:
|
|
function -- name of the function you want to execute
|
|
params -- dictionary of parameters for the function
|
|
|
|
Returns
|
|
A dictionary containing the function's results.
|
|
|
|
"""
|
|
# Add the API key parameter automatically
|
|
params['key'] = key
|
|
|
|
# Send the request
|
|
tries = 0
|
|
error = False
|
|
while tries <= retries:
|
|
try:
|
|
if method.lower() == 'post':
|
|
data = requests.post(base_url + function, json.dumps(data), params=params, headers={'content-type': 'application/json'})
|
|
elif method.lower() == 'delete':
|
|
data = requests.delete(base_url + function, params=params)
|
|
else:
|
|
data = requests.get(base_url + function, params=params)
|
|
|
|
# Exit out of the loop
|
|
break
|
|
except:
|
|
error = True
|
|
tries += 1
|
|
|
|
if error and tries >= retries:
|
|
raise APIError('Unable to connect to Shodan')
|
|
|
|
# Check that the API key wasn't rejected
|
|
if data.status_code == 401:
|
|
try:
|
|
raise APIError(data.json()['error'])
|
|
except:
|
|
pass
|
|
raise APIError('Invalid API key')
|
|
|
|
# Parse the text into JSON
|
|
try:
|
|
data = data.json()
|
|
except:
|
|
raise APIError('Unable to parse JSON response')
|
|
|
|
# Raise an exception if an error occurred
|
|
if type(data) == dict and data.get('error', None):
|
|
raise APIError(data['error'])
|
|
|
|
# Return the data
|
|
return data
|
|
|
|
|
|
def iterate_files(files, fast=False):
|
|
"""Loop over all the records of the provided Shodan output file(s)."""
|
|
from json import loads
|
|
if fast:
|
|
# Try to use ujson for parsing JSON if it's available and the user requested faster throughput
|
|
# It's significantly faster at encoding/ decoding JSON but it doesn't support as
|
|
# many options as the standard library. As such, we're mostly interested in using it for
|
|
# decoding since reading/ parsing files will use up the most time.
|
|
try:
|
|
from ujson import loads
|
|
except:
|
|
pass
|
|
|
|
if isinstance(files, basestring):
|
|
files = [files]
|
|
|
|
for filename in files:
|
|
# Create a file handle depending on the filetype
|
|
if filename.endswith('.gz'):
|
|
fin = gzip.open(filename, 'r')
|
|
else:
|
|
fin = open(filename, 'r')
|
|
|
|
for line in fin:
|
|
# Ensure the line has been decoded into a string to prevent errors w/ Python3
|
|
line = line.decode('utf-8')
|
|
|
|
# Convert the JSON into a native Python object
|
|
banner = loads(line)
|
|
yield banner
|
|
|
|
def get_screenshot(banner):
|
|
if 'opts' in banner and 'screenshot' in banner['opts']:
|
|
return banner['opts']['screenshot']
|
|
return None
|
|
|
|
|
|
def get_ip(banner):
|
|
if 'ipv6' in banner:
|
|
return banner['ipv6']
|
|
return banner['ip_str']
|
|
|
|
|
|
def open_file(filename, mode='a', compresslevel=9):
|
|
return gzip.open(filename, mode, compresslevel)
|
|
|
|
|
|
def write_banner(fout, banner):
|
|
line = json.dumps(banner) + '\n'
|
|
fout.write(line.encode('utf-8'))
|
|
|
|
|
|
def humanize_bytes(bytes, precision=1):
|
|
"""Return a humanized string representation of a number of bytes.
|
|
>>> humanize_bytes(1)
|
|
'1 byte'
|
|
>>> humanize_bytes(1024)
|
|
'1.0 kB'
|
|
>>> humanize_bytes(1024*123)
|
|
'123.0 kB'
|
|
>>> humanize_bytes(1024*12342)
|
|
'12.1 MB'
|
|
>>> humanize_bytes(1024*12342,2)
|
|
'12.05 MB'
|
|
>>> humanize_bytes(1024*1234,2)
|
|
'1.21 MB'
|
|
>>> humanize_bytes(1024*1234*1111,2)
|
|
'1.31 GB'
|
|
>>> humanize_bytes(1024*1234*1111,1)
|
|
'1.3 GB'
|
|
"""
|
|
|
|
if bytes == 1:
|
|
return '1 byte'
|
|
if bytes < 1024:
|
|
return '%.*f %s' % (precision, bytes, "bytes")
|
|
|
|
suffixes = ['KB', 'MB', 'GB', 'TB', 'PB']
|
|
multiple = 1024.0 #.0 force float on python 2
|
|
for suffix in suffixes:
|
|
bytes /= multiple
|
|
if bytes < multiple:
|
|
return '%.*f %s' % (precision, bytes, suffix)
|
|
return '%.*f %s' % (precision, bytes, suffix)
|