import shodan as shodan from cmd2 import with_argparser, with_category import argparse from prettytable import PrettyTable from mqtt_pwn.shell.base import BaseMixin from mqtt_pwn.config import SHODAN_API_KEY from mqtt_pwn.utils import shodan_key_required, export_table, import_shodan_table class ShodanMixin(BaseMixin): """Shodan Mixin Class""" shodan_parser = argparse.ArgumentParser( description="Search through the Shodan.io API to gather info on available MQTT brokers.") shodan_parser.add_argument('-c', '--cached', action='store_true', help='Get the last results previously fetched') @with_category(BaseMixin.CMD_CAT_GENERAL) @with_argparser(shodan_parser) def do_shodan(self, args): """The Shodan function method""" self._handle_shodan(args) @shodan_key_required def _handle_shodan(self, args): if args.cached: self._handle_shodan_from_cache() else: self._handle_shodan_from_api() def _handle_shodan_from_cache(self): table = import_shodan_table() if not table: self.print_error('No cached Shodan data yet') else: self.print_info('Fetching Shodan data from cache...') self.ppaged(msg=table) def _handle_shodan_from_api(self): api = shodan.Shodan(SHODAN_API_KEY) table = PrettyTable(field_names=[ 'IP', 'Port', 'ASN', 'Version', 'Organization', 'Hostnames' ]) table.align['ASN'] = "l" table.align['Organization'] = "l" table.align['Hostnames'] = "l" self.print_info('Fetching Shodan data...') for item in api.search_cursor('MQTT Connection Code: 0'): table.add_row([ item.get('ip_str', '-'), item.get('port', 0), item.get('asn', '-'), item.get('version', '-'), item.get('org', '-'), ', '.join(item.get('hostnames', [])) or '-', ]) self.ppaged(msg=str(table)) export_table(table)