I'm trying to write a Python script that will search the Shodan API and return ID, CVE and Description. As some of my search results ('java' for example) do not have an established CVE (or CVE key), my script chokes. I'm aware that I need to wrap the search in try/except error handling, but I've not had any luck with what I've been able to find researching the web. Here is the error I get and below that is the code. Thanks very much in advance.
--------Error-------
print '%s: %s: %s' % (exploit['id'], exploit['cve'], exploit['description'])
KeyError: 'cve
--------My Code------
from shodan import WebAPI
api = WebAPI("my shodan key")
user_selection = raw_input("Enter something: ")
print "searching for", (user_selection),"....."
results = api.exploitdb.search(user_selection)
for exploit in results['matches']:
print '%s: %s: %s' % (exploit['id'], exploit['cve'], exploit['description'])
It looks like you want to use dict.get and provide it a default value to be returned where the key doesn't exist:
print '%s: %s: %s' % (exploit.get('id', '[blank]'), exploit.get('cve', '[blank]'), exploit.get('description', '[blank]'))
Related
This is my code:
I import the modules
import shodan
import json
I create my key,
SHODAN_API_KEY = ('xxxxxxxxxxxxxxxxxxxx')
api = shodan.Shodan(SHODAN_API_KEY)
I open my json file,
with open('Ports.json', 'r') as f:
Ports_dict = json.load(f)
#I loop through my dict,
for Port in Ports_dict:
print(Port['Port_name'])
try:
results = api.search(Port['Port_name']) # how can I filter ports by country??
#and I print the content.
print('Results found: {}'.format(results['total']))
for result in results['matches']:
print('IP: {}'.format(result['ip_str']))
print(result['data'])
print('')
print ('Country_code: %s' % result['location']['country_code'])
except shodan.APIError as e:
print(' Error: %s' % e)
But how can I filter ports by country?
In order to filter the results you need to use a search filter. The following article explains the general search query syntax of Shodan:
https://help.shodan.io/the-basics/search-query-fundamentals
Here is a list of all available search filters:
https://beta.shodan.io/search/filters
And here is a page full of example search queries:
https://beta.shodan.io/search/examples
In your case, you would want to use the port and country filter. For example, the following search query returns the MySQL and PostgreSQL servers in the US:
https://beta.shodan.io/search?query=port%3A3306%2C5432+country%3AUS
I would also recommend using the Shodan CLI for downloading data as it will handle paging through results for you:
https://help.shodan.io/guides/how-to-download-data-with-api
If you need to do it yourself within Python then you would also need to loop through the search results either by providing a page parameter or by simply using the Shodan.search_cursor() method (instead of Shodan.search() as you did in your code). The above article also shows how to use the search_cursor() method.
NEVER MIND - I FOUND MY REAL ISSUE, IT WAS FURTHER ON IN MY CODE THAT I REALIZED.
I am having problems getting xml.etree.ElementTree to work like I expect it to.
xmlData = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><suggestedmatches><destination><sortOrder>1</sortOrder><destinationType>destinationType1</destinationType></destination><destination><sortOrder>2</sortOrder><destinationType>destinationType2</destinationType></destination></suggestedmatches>"
root = ET.fromstring(xmlData)
logging.debug("DIAG: %s: root.tag = %s"
% (FUNCTION_NAME, root.tag))
logging.debug("DIAG: %s: root = %r" % (FUNCTION_NAME, ET.tostring(root)))
destinations = root.findall("destination")
logging.debug('DIAG: %s: destinations = %r' % (FUNCTION_NAME, ET.tostring(destinations)))
I'm trying to figure out why I can't find destinations in root.
DEBUG:root:DIAG: findDestinations(): root.tag = suggestedmatches
DEBUG:root:DIAG: findDestinations(): root = b'<suggestedmatches><destination><sortOrder>1</sortOrder><destinationType>destinationType1</destinationType></destination><destination><sortOrder>2</sortOrder><destinationType>destinationType2</destinationType></destination></suggestedmatches>'
ERROR:root:findDestinations(): Encountered exception on root.findall() - 'list' object has no attribute 'iter'
And if I add the following code after I get root, I am seeing each of the destinations listed in the log:
for destination in root:
destinationList.append(destination)
logging.debug('DIAG: %s: destination.tag = %s'
% (FUNCTION_NAME, destination.tag))
This same code is working in a different script, so I'm not sure why it's not working here.
You are getting None because ET.dump writes to sys.stdout and you are logging return of dump which is None.
From docs:
xml.etree.ElementTree.dump(elem)
Writes an element tree or element structure to sys.stdout. This function should be used for debugging only.
The exact output format is implementation dependent. In this version, it’s written as an ordinary XML file.
elem is an element tree or an individual element.
Try using tostring method instead of dump.
logging.debug("DIAG: %s: root = %r" % (FUNCTION_NAME, ET.tostring(root)))
I want to list once my script in python search for specific strings , but I also want to add country code first two letters , but when I try then it says invalid KeyError: 'country_code', but the api says ocation.country_code how can I achieve that?
#!/usr/bin/python
import shodan
SHODAN_API_KEY="xxxxxxxxxxxxxxxxxxxx"
api = shodan.Shodan(SHODAN_API_KEY)
try:
# Search Shodan
results = api.search('ProFTPd-1.3.3c')
# Show the results
for result in results['matches']:
print '%s' % result['ip_str']
print '%s' % result['country_code']
except shodan.APIError, e:
print 'Error: %s' % e
I think this is the method You are using in Python
https://github.com/achillean/shodan-python/blob/master/shodan/client.py#L324
and it triggers:
return self._request('/shodan/host/search', args)
Shodan API documentation:
https://developer.shodan.io/api
check out /shodan/host/search API
I just saw that the answer is in Your question but You ate one letter from location (ocation).
Try this:
print '%s' % result['location']['country_code']
So field You are looking for is there but it is in another dictionary.
I would recommend to read API documentation well next time and as Nofal Daud said, Python error are self explanatory if You have KeyError on dict it means that field is not there. Next time listen to Python it will reveal the truth.
This code is in python and works just fine:
# tests if class exists in the dictionary attrs
try:
self.attrs['class']
# if it doesnt python will throw an exception
except KeyError:
self.attrs['class'] = "someclass"
# else it is defined, so we concat someclass to previous value
else:
self.attrs['class'] = "someclass %s" % self.attrs['class']
Although, I am afraid this is not a good practice, because if python have an update and changes the name of exception thrown, it may stop working. Is it a bad practice? Is there a better way of doing that?
You have a lot more lookups there than you need ... In this case, you could probably do something like:
self.attrs['class'] = 'someclass %s' % self.attrs.get('class','')
This will give you:
'someclass '
if the 'class' key isn't already in your dict however (which is one space different than before).
Why rely on exceptions when you can just use membership testing:
# tests if class exists in the dictionary attrs
if 'class' in self.attrs:
self.attrs['class'] = "someclass %s" % self.attrs['class']
else:
self.attrs['class'] = "someclass"
or at the very least, remove the noop try:
# tests if class exists in the dictionary attrs
try:
self.attrs['class'] = "someclass %s" % self.attrs['class']
except KeyError:
self.attrs['class'] = "someclass"
#mgilson's answer is great. This is another approach that maybe is easier to understand and solves the last space problem:
if 'class' in self.attrs:
self.attrs['class'] = "someclass %s" % self.attrs['class']
else:
self.attrs['class'] = "someclass"
This code is inefficient because on the success path it still looks up the original 'class' value twice. Try this instead
try:
v = self.attrs['class']
self.attrs['class'] = "someclass %s" % v
except KeyError:
self.attrs['class'] = "someclass"
The type of the exception won't change but there are two solutions that might be better:
You can use attrs.get('class', 'someclass'), i.e. provide a default value if the key can't be found
Extend UserDict and overwrite __missing__():
class ListDict(UserDict):
def __missing__(key):
return ['someclass']
That gives you a dictionary which automatically creates a new list for all missing keys. You can now use it like this:
self.attrs['class'].append(v)
I think the name of the exception shouldn't be changing, but in case you need to perform that kind of logic over a dictionary (to add a new element if it's not there or to update if it is), could be also implemented like the following (is an example, I hope it helps):
if not "class" in self.attrs:
self.attrs['class'] = 'newclass'
else:
self.attrs['class'] = "someclass %s" % self.attrs['class']
The first part of the conditional checks if the element is within the list of keys of the dict. That should be useful for you as a sort of "idiom".
Regards.
If you are creating the attrs dictionary yourself you could use a DefaultDict:
from collections import defaultdict
attrs = defaultdict(str)
attrs['class'] = "someclass %s" % attrs['class']
I'm trying to port an application from using HAL to using pure udev. It is written in python and will use the gudev library, though I would love to see examples in any language. I'm able to get all attached video devices (such as cameras) via:
import gudev
client = gudev.Client(["video4linux"])
for device in client.get_devices():
print device.get_sysfs_attr("name"), device.get_device_name()
This prints out something like:
USB2.0 UVC WebCam /dev/video0
I am also able to get a list of block devices, but how can I:
Tell if it is a CD/DVD drive?
Tell if media is currently inserted if the drive supports removable media?
Tell what the name/label of the media is (e.g. FUTURAMAS1 for a DVD)?
The original code I am trying to port over is located at http://github.com/danielgtaylor/arista/blob/045a4d48ebfda44bc5d0609618ff795604ee134f/arista/inputs.py
Any and all help would be greatly appreciated!
Update: adding answer below.
import gudev
client = gudev.Client(['block'])
for device in client.query_by_subsystem("block"):
if device.has_property("ID_CDROM"):
print "Found CD/DVD drive at %s" % device.get_device_file()
if device.has_property("ID_FS_LABEL"):
print "Found disc: %s" % device.get_property("ID_FS_LABEL")
elif device.has_property("ID_FS_TYPE"):
print "Found disc"
else:
print "No disc"
The code above will output data like:
Found CD/DVD drive at /dev/sr0
Found disc: Ubuntu_10.04_i386
Thanks for the help!
Have a look at the device properties:
import gudev
client = gudev.Client(['block'])
for device in client.query_by_subsystem("block"):
print device
for device_key in device.get_property_keys():
print " property %s: %s" % (device_key, device.get_property(device_key))
print