Skip to content

Commit

Permalink
shodan payload
Browse files Browse the repository at this point in the history
  • Loading branch information
xmendez committed Apr 10, 2019
1 parent a39e792 commit 6231727
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 1 deletion.
5 changes: 4 additions & 1 deletion src/wfuzz/facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ def get_config_file(self):

def set_defaults(self):
return dict(
plugins=[("bing_apikey", '')],
plugins=[
("bing_apikey", ''),
("shodan_apikey", '')
],
kbase=[("discovery.blacklist", '.svg-.css-.js-.jpg-.gif-.png-.jpeg-.mov-.avi-.flv-.ico')],
connection=[
("concurrent", '10'),
Expand Down
112 changes: 112 additions & 0 deletions src/wfuzz/plugin_api/payloadtools.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from wfuzz.exception import FuzzExceptMissingAPIKey, FuzzExceptResourceParseError
from wfuzz.facade import Facade
from wfuzz.utils import MyCounter


# Python 2 and 3: alternative 4
try:
Expand All @@ -13,6 +15,18 @@

# python 2 and 3: iterator
from builtins import object
from threading import Thread
from queue import Queue

import shodan

# TODO: test cases
m = {
'matches': [
{'_shodan': {'id': '54e0ae62-9e22-404b-91b4-92f99e89c987', 'options': {}, 'ptr': True, 'module': 'auto', 'crawler': '62861a86c4e4b71dceed5113ce9593b98431f89a'}, 'hash': -1355923443, 'os': None, 'ip': 1240853908, 'isp': 'Comcast Cable', 'http': {'html_hash': -2142469325, 'robots_hash': None, 'redirects': [], 'securitytxt': None, 'title': '400 Bad Request', 'sitemap_hash': None, 'robots': None, 'favicon': None, 'host': '73.245.237.148', 'html': '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">\n<html><head>\n<title>400 Bad Request</title>\n</head><body>\n<h1>Bad Request</h1>\n<p>Your browser sent a request that this server could not understand.<br />\nReason: You\'re speaking plain HTTP to an SSL-enabled server port.<br />\n Instead use the HTTPS scheme to access this URL, please.<br />\n</p>\n<p>Additionally, a 404 Not Found\nerror was encountered while trying to use an ErrorDocument to handle the request.</p>\n</body></html>\n', 'location': '/', 'components': {}, 'server': 'Apache', 'sitemap': None, 'securitytxt_hash': None}, 'port': 9445, 'hostnames': ['c-73-245-237-148.hsd1.fl.comcast.net'], 'location': {'city': 'Fort Lauderdale', 'region_code': 'FL', 'area_code': 954, 'longitude': -80.3704, 'country_code3': 'USA', 'country_name': 'United States', 'postal_code': '33331', 'dma_code': 528, 'country_code': 'US', 'latitude': 26.065200000000004}, 'timestamp': '2019-04-10T10:30:48.297701', 'domains': ['comcast.net'], 'org': 'Comcast Cable', 'data': 'HTTP/1.1 400 Bad Request\r\nDate: Wed, 10 Apr 2019 10:19:07 GMT\r\nServer: Apache\r\nContent-Length: 481\r\nConnection: close\r\nContent-Type: text/html; charset=iso-8859-1\r\n\r\n', 'asn': 'AS7922', 'transport': 'tcp', 'ip_str': '73.245.237.148'},
{'_shodan': {'id': '4ace6fd1-8295-4aea-a086-2280598ca9e7', 'options': {}, 'ptr': True, 'module': 'auto', 'crawler': '62861a86c4e4b71dceed5113ce9593b98431f89a'}, 'product': 'Apache httpd', 'hash': 370611044, 'os': None, 'ip': 35226500, 'isp': 'EE High Speed Internet', 'http': {'html_$ ash': -163723763, 'robots_hash': None, 'redirects': [], 'securitytxt': None, 'title': '401 Authorization Required', 'sitemap_hash': None, 'robots': None, 'favicon': None, 'host': '2.25.131.132', 'html': '<HEAD><TITLE>401 Authorization Required</TITLE></HEAD>\n<BODY><H1>401 Authoriza$ ion Required</H1>\nBrowser not authentication-capable or authentication failed.\n</BODY>\n', 'location': '/', 'components': {}, 'server': 'Apache', 'sitemap': None, 'securitytxt_hash': None}, 'cpe': ['cpe:/a:apache:http_server'], 'port': 8085, 'hostnames': [], 'location': {'city': '$ helmsford', 'region_code': 'E4', 'area_code': None, 'longitude': 0.48330000000001405, 'country_code3': 'GBR', 'country_name': 'United Kingdom', 'postal_code': 'CM2', 'dma_code': None, 'country_code': 'GB', 'latitude': 51.733300000000014}, 'timestamp': '2019-04-10T11:03:59.955967', '$ omains': [], 'org': 'EE High Speed Internet', 'data': 'HTTP/1.1 401 Unauthorized\r\nServer: Apache\r\nConnection: Close\r\nContent-type: text/html\r\nWWW-Authenticate: Digest realm="DSLForum CPE Management", algorithm=MD5, qop=auth, stale=FALSE, nonce="3d7a3f71e72e095dba31fd77d4db74$5", opaque="5ccc069c403ebaf9f0171e9517f40e41"\r\n\r\n', 'asn': 'AS12576', 'transport': 'tcp', 'ip_str': '2.25.131.132'},
]
}


class BingIter(object):
Expand Down Expand Up @@ -115,3 +129,101 @@ def __next__(self):
return elem.encode('utf-8')
else:
return elem


class ShodanIter():
SHODAN_RES_PER_PAGE = 100
MAX_ENQUEUED_RES = SHODAN_RES_PER_PAGE + 1
NUM_OF_WORKERS = 1
SLOW_START = True

def __init__(self, dork, page, limit):
key = Facade().sett.get('plugins', 'shodan_apikey')
if not key:
raise FuzzExceptMissingAPIKey("A Shodan api key is needed. Please check ~/.wfuzz/wfuzz.ini")

self.api = shodan.Shodan(key)
self._dork = dork
self._page = MyCounter(page)

self.results_queue = Queue(self.MAX_ENQUEUED_RES)
self.page_queue = Queue()

self._threads = []

self._started = False
self._cancel_job = False

def _do_search(self):
while 1:
page = self.page_queue.get()
if page is None:
self.page_queue.task_done()
break

if self._cancel_job:
self.page_queue.task_done()
continue

try:
results = self.api.search(self._dork, page=page)
for item in results['matches']:
if not self._cancel_job:
self.results_queue.put(item)

self.page_queue.task_done()
if not self._cancel_job:
self.page_queue.put(self._page.inc())
except shodan.APIError as e:
self.page_queue.task_done()
self.results_queue.put(e)
continue

def __iter__(self):
return self

def _start(self):
for th_n in range(self.NUM_OF_WORKERS):
worker = Thread(target=self._do_search)
worker.setName('_do_search_{}'.format(str(th_n)))
self._threads.append(worker)
worker.start()

self.page_queue.put(self._page())
if not self.SLOW_START:
for _ in range(self.NUM_OF_WORKERS - 1):
self.page_queue.put(self._page.inc())

def _stop(self):
self._cancel_job = True

for th in self._threads:
self.page_queue.put(None)

self.page_queue.join()

for th in self._threads:
th.join()

self._threads = []

self.results_queue.put(None)
self._cancel_job = False
self._started = False

def __next__(self):
if not self._started:
self._start()
self._started = True

res = self.results_queue.get()
self.results_queue.task_done()

if res is None:
self._stop()
raise StopIteration
elif isinstance(res, Exception):
self._stop()
raise res

return res
51 changes: 51 additions & 0 deletions src/wfuzz/plugins/payloads/shodanp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from wfuzz.externals.moduleman.plugin import moduleman_plugin
from wfuzz.plugin_api.payloadtools import ShodanIter
from wfuzz.plugin_api.base import BasePayload


@moduleman_plugin
class shodanp(BasePayload):
name = "shodanp"
author = ("Xavi Mendez (@xmendez)",)
version = "0.1"
description = (
"Queries the Shodan API",
)

summary = "Returns hostnames or IPs of a given Shodan API search (needs api key)."
category = ["default"]
priority = 99

parameters = (
("search", "", True, "Shodan search string."),
("page", "0", False, "Offset page, starting at zero."),
# TODO: ("limit", "0", False, "Number of results (1 query credit = 100 results). Zero for all."),
)

default_parameter = "search"

def __init__(self, params):
BasePayload.__init__(self, params)

search = params["search"]
page = int(params["page"])
limit = int(params["limit"])

self._it = ShodanIter(search, page, limit)

def __iter__(self):
return self

def count(self):
return -1

def close(self):
self._it._stop()

def __next__(self):
match = next(self._it)
if match['hostnames']:
for hostname in match['hostnames']:
return hostname
else:
return match['ip_str']

0 comments on commit 6231727

Please sign in to comment.