Skip to content

Commit

Permalink
efield/field cli options
Browse files Browse the repository at this point in the history
  • Loading branch information
xmendez committed Apr 15, 2019
1 parent 62527e2 commit cb509e8
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 24 deletions.
10 changes: 8 additions & 2 deletions docs/user/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ These timeouts are really handy when you are using Wfuzz to bruteforce resources
Filter Language
---------------

Wfuzz's filter language grammar is build using `pyparsing <http://pyparsing.wikispaces.com/>`_, therefore it must be installed before using the command line parameters "--filter, --prefilter, --slice, --field".
Wfuzz's filter language grammar is build using `pyparsing <http://pyparsing.wikispaces.com/>`_, therefore it must be installed before using the command line parameters "--filter, --prefilter, --slice, --field and --efield".

The information about the filter language can be also obtained executing::

Expand Down Expand Up @@ -745,5 +745,11 @@ Authtoken is the parameter used by BEA WebLogic Commerce Servers (TM) as a CSRF
You can also select the field to show, for example::

$ wfpayload -z wfuzzp --zD /tmp/session --field r.params.get
000000002: 200 118 L 455 W 5384 Ch "{'artist': '1'}"
artist=5
...

Or::

$ wfpayload -z wfuzzp --zD /tmp/session --efield r.params.get
000000006: 200 99 L 272 W 3868 Ch "5 | artist=5"
...
17 changes: 11 additions & 6 deletions src/wfuzz/fuzzobjects.py
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,7 @@ def __init__(self, history=None, exception=None, track_id=True):
self.payload = []

self._description = None
self._show_field = False

@property
def plugins(self):
Expand Down Expand Up @@ -747,8 +748,7 @@ def __str__(self):
else:
return "Control result, type: %s" % ("seed", "backfeed", "result", "error", "startseed", "endseed", "cancel", "discarded")[self.type]

@property
def description(self):
def _payload_description(self):
if not self.payload:
return self.url

Expand All @@ -760,11 +760,14 @@ def description(self):

return ret_str

def get_full_description(self):
if self._description is not None:
return "{} | {}".format(self.description, self.eval(self._description))
@property
def description(self):
if self._show_field is True:
return self.eval(self._description)
elif self._show_field is False and self._description is not None:
return "{} | {}".format(self._payload_description(), self.eval(self._description))

return self.description
return self._payload_description()

def eval(self, expr):
return FuzzResFilter(filter_string=expr).is_visible(self)
Expand Down Expand Up @@ -814,11 +817,13 @@ def from_soft_copy(self, track_id=True):
fr.rlevel = self.rlevel
fr.payload = list(self.payload)
fr._description = self._description
fr._show_field = self._show_field

return fr

def update_from_options(self, options):
self._description = options['description']
self._show_field = options['show_field']

@staticmethod
def to_new_exception(exception):
Expand Down
3 changes: 2 additions & 1 deletion src/wfuzz/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
class FuzzSession(UserDict):
def __init__(self, **kwargs):
self.data = self._defaults()
self.keys_not_to_dump = ["interactive", "recipe", "seed_payload", "send_discarded", "compiled_genreq", "compiled_filter", "compiled_prefilter", "compiled_printer", "description"]
self.keys_not_to_dump = ["interactive", "recipe", "seed_payload", "send_discarded", "compiled_genreq", "compiled_filter", "compiled_prefilter", "compiled_printer", "description", "show_field"]

# recipe must be superseded by options
if "recipe" in kwargs and kwargs["recipe"]:
Expand Down Expand Up @@ -87,6 +87,7 @@ def _defaults(self):
connect_to_ip=None,
description=None,
no_cache=False,
show_field=None,

# this is equivalent to payloads but in a different format
dictio=None,
Expand Down
8 changes: 7 additions & 1 deletion src/wfuzz/ui/console/clparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from .output import table_print

short_opts = "hLAZX:vcb:e:R:d:z:r:f:t:w:V:H:m:f:o:s:p:w:u:"
long_opts = ['no-cache', 'ee=', 'zE=', 'zD=', 'field=', 'ip=', 'filter-help', 'AAA', 'AA', 'slice=', 'zP=', 'oF=', 'recipe=', 'dump-recipe=', 'req-delay=', 'conn-delay=', 'sc=', 'sh=', 'sl=', 'sw=', 'ss=', 'hc=', 'hh=', 'hl=', 'hw=', 'hs=', 'ntlm=', 'basic=', 'digest=', 'follow', 'script-help=', 'script=', 'script-args=', 'prefilter=', 'filter=', 'interact', 'help', 'version', 'dry-run', 'prev']
long_opts = ['efield=', 'no-cache', 'ee=', 'zE=', 'zD=', 'field=', 'ip=', 'filter-help', 'AAA', 'AA', 'slice=', 'zP=', 'oF=', 'recipe=', 'dump-recipe=', 'req-delay=', 'conn-delay=', 'sc=', 'sh=', 'sl=', 'sw=', 'ss=', 'hc=', 'hh=', 'hl=', 'hw=', 'hs=', 'ntlm=', 'basic=', 'digest=', 'follow', 'script-help=', 'script=', 'script-args=', 'prefilter=', 'filter=', 'interact', 'help', 'version', 'dry-run', 'prev']


class CLParser:
Expand Down Expand Up @@ -484,6 +484,12 @@ def _parse_seed(self, url, optsd, options):

if "--field" in optsd:
options['description'] = optsd["--field"][0]
options["show_field"] = True
elif "--efield" in optsd:
options['description'] = optsd["--efield"][0]
options["show_field"] = False
else:
options["show_field"] = None

if "--ip" in optsd:
splitted = optsd["--ip"][0].partition(":")
Expand Down
3 changes: 2 additions & 1 deletion src/wfuzz/ui/console/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@
\t--interact : (beta) If selected,all key presses are captured. This allows you to interact with the program.
\t--dry-run : Print the results of applying the requests without actually making any HTTP request.
\t--prev : Print the previous HTTP requests (only when using payloads generating fuzzresults)
\t--field <expr> : Show the specified language expression together with the current payload
\t--efield <expr> : Show the specified language expression together with the current payload
\t--field <expr> : Do not show the payload but only the specified language expression
\t
\t-p addr : Use Proxy in format ip:port:type. Repeat option for using various proxies.
\t Where type could be SOCKS4,SOCKS5 or HTTP if omitted.
Expand Down
4 changes: 2 additions & 2 deletions src/wfuzz/ui/console/mvc.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def _print_verbose(self, res, print_nres=True):
("%d Ch" % res.chars, txt_colour),
(server, txt_colour),
(location, txt_colour),
("\"%s\"" % res.get_full_description(), txt_colour),
("\"%s\"" % res.description, txt_colour),
]

self.term.set_colour(txt_colour)
Expand Down Expand Up @@ -216,7 +216,7 @@ def _print(self, res, print_nres=True):
("%d L" % res.lines, txt_colour),
("%d W" % res.words, txt_colour),
("%d Ch" % res.chars, txt_colour),
("\"%s\"" % res.get_full_description(), txt_colour),
("\"%s\"" % res.description, txt_colour),
]

self.term.set_colour(txt_colour)
Expand Down
29 changes: 19 additions & 10 deletions src/wfuzz/wfuzz.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ def usage():
\t--slice <filter> : Filter payload\'s elements using the specified expression.
\t-w wordlist : Specify a wordlist file (alias for -z file,wordlist).
\t-m iterator : Specify an iterator for combining payloads (product by default)
\t--field <field> : Show a FuzzResult field instead of current payload
\t--field <expr> : Do not show the payload but the specified language expression
\t--efield <expr> : Show the specified language expression together with the current payload
""")

# TODO: from .api import payload
Expand All @@ -88,7 +89,7 @@ def usage():
import getopt

try:
opts, args = getopt.getopt(sys.argv[1:], "vhz:m:w:", ["field=", "help", "slice=", "zD=", "zP="])
opts, args = getopt.getopt(sys.argv[1:], "vhz:m:w:", ["field=", "help", "slice=", "zD=", "zP=", "efield="])
except getopt.GetoptError as err:
print((str(err)))
usage()
Expand All @@ -99,12 +100,17 @@ def usage():
sys.exit()

field = None
raw_output = False

for o, value in opts:
if o in ("-h", "--help"):
usage()
sys.exit()
if o in ("--efield"):
field = value
if o in ("--field"):
field = value
raw_output = True

session = None

Expand All @@ -120,16 +126,19 @@ def usage():
else:
r = res[0]

# TODO: option to not show headers in fuzzres
# TODO: all should be same object type and no need for isinstance
if isinstance(r, FuzzResult):
if printer is None:
printer = View(session_options)
printer.header(None)

if field:
r._description = field
printer.result(r)
if raw_output:
print(r.eval(field if field is not None else "url"))
else:
if printer is None:
printer = View(session_options)
printer.header(None)

if field:
r._description = field
r._show_field = False
printer.result(r)
else:
print(r)

Expand Down
57 changes: 56 additions & 1 deletion tests/test_api.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import unittest
import sys
from io import BytesIO
import gzip
import pickle as pickle

import wfuzz
from wfuzz.facade import Facade
from wfuzz.fuzzobjects import FuzzRequest
from wfuzz.fuzzobjects import FuzzResult

try:
# Python >= 3.3
Expand Down Expand Up @@ -64,6 +70,56 @@ def test_get_session(self):
self.assertEqual(data.get('url'), 'http://127.0.0.1/FUZZ')
self.assertEqual(data.get('payloads'), [('range', {'default': '0-4', 'encoder': None}, None)])

def test_payload_description(self):
class mock_saved_session(object):
def __init__(self, description, show_field):
fr = FuzzRequest()
fr.url = "http://www.wfuzz.org/path?param=1&param2=2"
fuzz_res = FuzzResult(history=fr)
fuzz_res._description = description
fuzz_res._show_field = show_field

self.outfile = BytesIO()

with gzip.GzipFile(fileobj=self.outfile, mode="wb") as f:
pickle.dump(fuzz_res, f)

self.outfile.seek(0)
self.outfile.name = "mockfile"

def close(self):
pass

def read(self, pos):
return self.outfile.read(pos)

# load plugins before mocking file object
Facade().payloads

m = mock.MagicMock(name='open', spec=open)
m.return_value = mock_saved_session("r.params.all", True)

mocked_fun = "builtins.open" if sys.version_info >= (3, 0) else "__builtin__.open"
with mock.patch(mocked_fun, m):
payload_list = list(wfuzz.payload(**{'show_field': True, 'description': 'r', 'payloads': [('wfuzzp', {'default': 'mockedfile', 'encoder': None}, None)]}))
self.assertEqual([res[0].description for res in payload_list], [{'param': '1', 'param2': '2'}])

m = mock.MagicMock(name='open', spec=open)
m.return_value = mock_saved_session("url", None)

mocked_fun = "builtins.open" if sys.version_info >= (3, 0) else "__builtin__.open"
with mock.patch(mocked_fun, m):
payload_list = list(wfuzz.payload(**{'show_field': True, 'description': 'r', 'payloads': [('wfuzzp', {'default': 'mockedfile', 'encoder': None}, None)]}))
self.assertEqual([res[0].description for res in payload_list], ['http://www.wfuzz.org/path?param=1&param2=2'])

m = mock.MagicMock(name='open', spec=open)
m.return_value = mock_saved_session("r.scheme", False)

mocked_fun = "builtins.open" if sys.version_info >= (3, 0) else "__builtin__.open"
with mock.patch(mocked_fun, m):
payload_list = list(wfuzz.payload(**{'show_field': True, 'description': 'r', 'payloads': [('wfuzzp', {'default': 'mockedfile', 'encoder': None}, None)]}))
self.assertEqual([res[0].description for res in payload_list], ['http://www.wfuzz.org/path?param=1&param2=2 | http'])

def test_payload(self):
payload_list = list(wfuzz.payload(**{'payloads': [('range', {'default': '0-4', 'encoder': None}, None)]}))
self.assertEqual(payload_list, [('0',), ('1',), ('2',), ('3',), ('4',)])
Expand Down Expand Up @@ -95,7 +151,6 @@ def seek(self, pos):
next = __next__ # for Python 2

m = mock.MagicMock(name='open', spec=open)
m.return_value = iter([b"one", b"two"])
m.return_value = mock_file()

mocked_fun = "builtins.open" if sys.version_info >= (3, 0) else "__builtin__.open"
Expand Down

0 comments on commit cb509e8

Please sign in to comment.