Python Shodan API Search 03-02-2014, 04:25 AM
#1
Love shodan. Think this is a great tool. Make sure you register and put in your API Key, or else this script wont work...
http://shodanhq.com
http://shodanhq.com
Code:
#!/usr/bin/python2
import os
import sys
import signal
import time
#Please set your api key here in between the qoute marks.
userapi = "tDUKzBYL52cAxDt3ObWRLeazSokWRP"
#yep right up there
try:
from json import dumps, loads
except:
from simplejson import dumps, loads
try:
# Python 2
from urllib2 import urlopen
from urllib import urlencode
except:
# Python 3
from urllib.request import urlopen
from urllib.parse import urlencode
__all__ = ['WebAPI']
class WebAPIError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return self.value
class WebAPI:
"""Wrapper around the SHODAN webservices API"""
class Exploits:
def __init__(self, parent):
self.parent = parent
def search(self, query, sources=[], cve=None, osvdb=None, msb=None, bid=None):
"""Search the entire Shodan Exploits archive using the same query syntax
as the website.
Arguments:
query -- exploit search query; same syntax as website
Optional arguments:
sources -- metasploit, cve, osvdb, exploitdb, or packetstorm
cve -- CVE identifier (ex. 2010-0432)
osvdb -- OSVDB identifier (ex. 11666)
msb -- Microsoft Security Bulletin ID (ex. MS05-030)
bid -- Bugtraq identifier (ex. 13951)
"""
if sources:
query += ' source:' + ','.join(sources)
if cve:
query += ' cve:%s' % (str(cve).strip())
if osvdb:
query += ' osvdb:%s' % (str(osvdb).strip())
if msb:
query += ' msb:%s' % (str(msb).strip())
if bid:
query += ' bid:%s' % (str(bid).strip())
return self.parent._request('search_exploits', {'q': query})
class ExploitDb:
def __init__(self, parent):
self.parent = parent
def download(self, id):
"""Download the exploit code from the ExploitDB archive.
Arguments:
id -- ID of the ExploitDB entry
Returns:
A dictionary with the following fields:
filename -- Name of the file
content-type -- Mimetype
data -- Contents of the file
"""
return self.parent._request('exploitdb/download', {'id': id})
def search(self, query, **kwargs):
"""Search the ExploitDB archive.
Arguments:
query -- Search terms
Optional arguments:
author -- Name of the exploit submitter
platform -- Target platform (e.g. windows, linux, hardware etc.)
port -- Service port number
type -- Any, dos, local, papers, remote, shellcode and webapps
Returns:
A dictionary with 2 main items: matches (list) and total (int).
Each item in 'matches' is a dictionary with the following elements:
id
author
date
description
platform
port
type
"""
return self.parent._request('exploitdb/search', dict(q=query, **kwargs))
class Msf:
def __init__(self, parent):
self.parent = parent
def download(self, id):
"""Download a metasploit module given the fullname (id) of it.
Arguments:
id -- fullname of the module (ex. auxiliary/admin/backupexec/dump)
Returns:
A dictionary with the following fields:
filename -- Name of the file
content-type -- Mimetype
data -- File content
"""
return self.parent._request('msf/download', {'id': id})
def search(self, query, **kwargs):
"""Search for a Metasploit module.
"""
return self.parent._request('msf/search', dict(q=query, **kwargs))
def __init__(self, key):
"""Initializes the API object.
Arguments:
key -- your API key
"""
self.api_key = key
self.base_url = 'http://www.shodanhq.com/api/'
self.exploits = self.Exploits(self)
self.exploitdb = self.ExploitDb(self)
self.msf = self.Msf(self)
def _request(self, function, params):
"""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 JSON string containing the function's results.
"""
# Add the API key parameter automatically
params['key'] = self.api_key
# Send the request
data = urlopen(self.base_url + function + '?' + urlencode(params)).read().decode('utf-8')
# Parse the text into JSON
data = loads(data)
# Raise an exception if an error occurred
if data.get('error', None):
raise WebAPIError(data['error'])
# Return the data
return data
def count(self, query):
"""Returns the total number of search results for the query.
"""
return self._request('count', {'q': query})
def locations(self, query):
"""Return a break-down of all the countries and cities that the results for
the given search are located in.
"""
return self._request('locations', {'q': query})
def fingerprint(self, banner):
"""Determine the software based on the banner.
Arguments:
banner - HTTP banner
Returns:
A list of software that matched the given banner.
"""
return self._request('fingerprint', {'banner': banner})
def host(self, ip):
"""Get all available information on an IP.
Arguments:
ip -- IP of the computer
Returns:
All available information SHODAN has on the given IP,
subject to API key restrictions.
"""
return self._request('host', {'ip': ip})
def info(self):
"""Returns information about the current API key, such as a list of add-ons
and other features that are enabled for the current user's API plan.
"""
return self._request('info', {})
def search(self, query, page=1, limit=None, offset=None):
"""Search the SHODAN database.
Arguments:
query -- search query; identical syntax to the website
Optional arguments:
page -- page number of the search results
limit -- number of results to return
offset -- search offset to begin getting results from
Returns:
A dictionary with 3 main items: matches, countries and total.
Visit the website for more detailed information.
"""
args = {
'q': query,
'p': page,
}
if limit:
args['l'] = limit
if offset:
args['o'] = offset
return self._request('search', args)
#ending of useless bullshit
if len(sys.argv) <= 1:
arewescanmode = 'no'
elif len(sys.argv) > 1:
if sys.argv[1] == '--scan-mode':
arewescanmode = '--scan-mode'
if sys.argv[1] == '--help' or sys.argv[1] == '-h':
print '''To use this script you must first set your API key into the script by placing it in the
variable. Then you can use the script to gather information about IPs using the shodan
database. You can just run the script normally without any flags or you can run it with
the --scan-mode to flag to take the list of IP's and scan them with nmap. nmap must be
installed on your system. '''
exit()
if userapi == "":
exit( 'You api key is not set. Please open the program and put your api key in the userapi variable.' )
api = WebAPI(userapi)
#settings all the variables for the program.
if arewescanmode != '--scan-mode':
shodansearch = raw_input( 'What would you like to search?:' )
shodantotal = raw_input( 'Would you like to show the total results found? [y/n]:' )
shodancountry = raw_input( 'Would you like to show the country? [y/n]:' )
shodanhostname = raw_input( 'Would you like to show the hostname? [y/n]:' )
shodanos = raw_input( 'Would you like to show the os? [y/n]:' )
shodanport = raw_input( 'Would you like to show the port? [y/n]:' )
shodanupdated = raw_input( 'Would you like to show when target was last updated? [y/n]:' )
shodandata = raw_input( 'Would you like to show banner data? [y/n]:' )
shodanfileopt = raw_input( 'Would you like to save the output to a file? [y/n]:' )
elif arewescanmode == '--scan-mode':
shodansearch = raw_input( 'What would you like to search?:' )
if arewescanmode != '--scan-mode':
if shodanfileopt == 'y':
shodanfile = raw_input( 'Please enter in the path to an existing file. This will overwrite it:' )
while True:
if not os.path.exists(shodanfile):
shodanfile = raw_input( 'File does not exist. Try again:' )
else:
editshodanfile = open( shodanfile, 'w' )
break
#sets the initial try statement to grab all the results
try:
# search shodan
results = api.search(shodansearch)
# show the results if not in scan mode
if arewescanmode != '--scan-mode':
if shodansearch == 'y':
print 'Results found: %s' % results['total']
if shodanfileopt == 'y':
editshodanfile.write( 'Results found: %s\n' % results['total'] )
for result in results['matches']:
print 'IP: %s' % result['ip']
if shodancountry == 'y':
print 'Country: %s' % result['country_name']
if shodanhostname == 'y':
print 'Hostname: %s' % result['hostnames']
if shodanos == 'y':
print 'OS: %s' % result['os']
if shodanport == 'y':
print 'Port: %s' % result['port']
if shodanupdated == 'y':
print 'Updated: %s' % result['updated']
if shodandata == 'y':
print '\n%s' % result['data']
if shodanfileopt == 'y':
editshodanfile.write( 'IP: %s\n' % result['ip'] )
if shodancountry == 'y':
editshodanfile.write( 'Country: %s\n' % result['country_name'] )
if shodanhostname == 'y':
editshodanfile.write( 'Hostname: %s\n' % result['hostnames'] )
if shodanos == 'y':
editshodanfile.write( 'OS: %s\n' % result['os'] )
if shodanport == 'y':
editshodanfile.write( 'Port: %s\n' % result['port'] )
if shodanupdated == 'y':
editshodanfile.write( 'Updated: %s\n' % result['updated'] )
if shodandata == 'y':
editshodanfile.write( '\n%s' % result['data'] )
except Exception, e:
print 'Error: %s' % e
#if in scan mode then this menu is used instead
if arewescanmode == '--scan-mode':
scanfileopt = raw_input( 'Would you like to save output to a file? [y/n]:' )
if scanfileopt == 'y':
scanfile = raw_input( 'Please enter in the path to an existing file. This will overwrite it:' )
while True:
if not os.path.exists(scanfile):
scanfile = raw_input( 'File does not exist. Try again:' )
else:
editshodanfile = open( scanfile, 'w' )
tempscanfile = scanfile+'-temp'
fin = open(tempscanfile, "r")
break
os.system('clear')
#finds the estimated time
resultsinseconds = (results['total']*130)
if resultsinseconds >= 60 and resultsinseconds < 3600:
estimatedtime = resultsinseconds/60
print 'Estimated time to complete scanning all hosts: >%s minutes' % estimatedtime
elif resultsinseconds >= 3600 and resultsinseconds < 86400:
estimatedtime = (resultsinseconds/60)/24
print 'Estimated time to complete scanning all hosts: >%s days' % estimatedtime
elif resultsinseconds >= 86400 and resultsinseconds < 604800:
estimatedtime = ((resultsinseconds/60)/24)/7
print 'Estimated time to complete scanning all hosts: >%s weeks' % estimatedtime
elif resultsinseconds >= 604800 and resultsinseconds < 18144000:
estimatedtime = (((resultsinseconds/60)/24)/7)/30
print 'Estimated time to complete scanning all hosts: >%s months' % estimatedtime
elif resultsinseconds >= 18144000 and resultsinseconds < 217728000:
estimatedtime = ((((resultsinseconds/60)/24)/7)/30)/12
print 'Estimated time to complete scanning all hosts: >%s years' % estimatedtime
elif resultsinseconds > 217728000:
print 'Estimated time to complete scanning all hosts: Unknown'
#sets the menu and starts the scanning
def scanner(ignore):
if ignore == 'ignoring':
os.system( 'nmap '+result['ip']+' -Pn --host-timeout 2m')
else:
os.system( 'nmap '+result['ip']+' --host-timeout 2m')
print '\nWaiting for 10 seconds. If you want to pause the scan and see the menu then press ctrl-c now.'
time.sleep(10)
def scannerfile(ignore):
if ignore == 'ignoring':
os.system( 'nmap '+result['ip']+' --host-timeout 2m'+' -Pn -oN '+tempscanfile)
else:
os.system( 'nmap '+result['ip']+' --host-timeout 2m'+' -oN '+tempscanfile)
for line in fin.readlines():
editshodanfile.write(line)
print '\nWaiting for 10 seconds. If you want to pause the scan and see the menu then press ctrl-c now.'
time.sleep(10)
for result in results['matches']:
try:
print '#'*10+'Target Information'+'#'*10
print 'IP: %s' % result['ip']
print 'Country: %s' % result['country_name']
print 'Hostname: %s' % result['hostnames']
print 'OS: %s' % result['os']
print 'Updated: %s' % result['updated']
print '#'*10+'Nmap Output'+'#'*10
if scanfileopt != 'y':
scanner('foo')
if scanfileopt == 'y':
editshodanfile.write( '#'*10+'Target Information'+'#'*10+'\n' )
editshodanfile.write( 'IP: %s\n' % result['ip'] )
editshodanfile.write( 'Country: %s\n' % result['country_name'] )
editshodanfile.write( 'Hostname: %s\n' % result['hostnames'] )
editshodanfile.write( 'OS: %s\n' % result['os'] )
editshodanfile.write( 'Updated: %s\n' % result['updated'] )
editshodanfile.write( '#'*10+'Nmap Output'+'#'*10+'\n' )
scannerfile('foo')
editshodanfile.write( '\n' )
except KeyboardInterrupt:
print '\nMenu options: '
print 'rescan rescan the target using the -Pn flag'
print ' WARNING: '
print ' after running rescan you won\'t be '
print ' able to access the menu until another'
print ' ip is scanned. If you do it will '
print ' exit and delete all content in log '
print ' file is option is chose.\n '
print 'exit exit the scan '
print 'continue continue the scan '
menuopt = raw_input( ':' )
if menuopt == 'rescan':
if scanfileopt != 'y':
scanner( 'ignoring' )
elif scanfileopt == 'y':
scannerfile( 'ignoring' )
elif menuopt == 'exit':
if scanfileopt == 'y':
editshodanfile.close()
fin.close()
print '\nExiting...'
exit()
else:
print 'Continuing...'
if scanfileopt == 'y':
editshodanfile.close()
fin.close()