diff -Nru wfuzz-2.4/debian/changelog wfuzz-2.4.5/debian/changelog --- wfuzz-2.4/debian/changelog 2019-05-03 09:43:02.000000000 +0000 +++ wfuzz-2.4.5/debian/changelog 2020-02-06 10:34:44.000000000 +0000 @@ -1,3 +1,18 @@ +wfuzz (2.4.5-1) unstable; urgency=medium + + * Team upload. + + [ Samuel Henrique ] + * Add salsa-ci.yml + + [ Sophie Brun ] + * New upstream version 2.4.5 + * Add restrictions on python3-pycurl (temporary fix for + https://github.com/xmendez/wfuzz/issues/180) + * Refresh debian/patches + + -- Sophie Brun Thu, 06 Feb 2020 11:34:44 +0100 + wfuzz (2.4-1) unstable; urgency=medium * Team upload. diff -Nru wfuzz-2.4/debian/control wfuzz-2.4.5/debian/control --- wfuzz-2.4/debian/control 2019-05-03 09:43:02.000000000 +0000 +++ wfuzz-2.4.5/debian/control 2020-02-06 10:34:44.000000000 +0000 @@ -3,7 +3,7 @@ Priority: optional Maintainer: Debian Security Tools Uploaders: Hugo Lefeuvre -Build-Depends: debhelper-compat (= 11), +Build-Depends: debhelper-compat (= 12), dh-python, python3, python3-setuptools, @@ -12,14 +12,14 @@ python3-pycurl, python3-pyparsing, python3-six, -Standards-Version: 4.3.0 +Standards-Version: 4.5.0 Homepage: http://www.edge-security.com/wfuzz.php Vcs-Git: https://salsa.debian.org/pkg-security-team/wfuzz.git Vcs-Browser: https://salsa.debian.org/pkg-security-team/wfuzz Package: wfuzz Architecture: all -Depends: python3-pycurl, +Depends: python3-pycurl (<= 7.43.0.3), python3-pyparsing, ${misc:Depends}, ${python3:Depends} diff -Nru wfuzz-2.4/debian/patches/add-sys-path-append wfuzz-2.4.5/debian/patches/add-sys-path-append --- wfuzz-2.4/debian/patches/add-sys-path-append 2019-05-03 09:43:02.000000000 +0000 +++ wfuzz-2.4.5/debian/patches/add-sys-path-append 2020-02-06 10:34:44.000000000 +0000 @@ -1,7 +1,16 @@ -Description: Add sys.path information to wfuzz.py. - Add sys.path.append to the main script to make it look - under /usr/share/wfuzz/ for the rest of the modules it - needs. +From: Debian Security Tools +Date: Thu, 6 Feb 2020 11:34:12 +0100 +Subject: Add sys.path information to wfuzz.py. + +Add sys.path.append to the main script to make it look +under /usr/share/wfuzz/ for the rest of the modules it +needs. +--- + src/wfuzz/wfuzz.py | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/wfuzz/wfuzz.py b/src/wfuzz/wfuzz.py +index b62d7a0..1d8e7c3 100644 --- a/src/wfuzz/wfuzz.py +++ b/src/wfuzz/wfuzz.py @@ -1,5 +1,6 @@ diff -Nru wfuzz-2.4/debian/patches/fix-header-script.patch wfuzz-2.4.5/debian/patches/fix-header-script.patch --- wfuzz-2.4/debian/patches/fix-header-script.patch 2019-05-03 09:43:02.000000000 +0000 +++ wfuzz-2.4.5/debian/patches/fix-header-script.patch 2020-02-06 10:34:44.000000000 +0000 @@ -1,6 +1,14 @@ -Description: Add needed shebang to TextParser.py. -Author: Hugo Lefeuvre +From: Hugo Lefeuvre +Date: Thu, 6 Feb 2020 11:34:12 +0100 +Subject: Add needed shebang to TextParser.py. + Last-Update: 2016-07-16 +--- + src/wfuzz/externals/reqresp/TextParser.py | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/wfuzz/externals/reqresp/TextParser.py b/src/wfuzz/externals/reqresp/TextParser.py +index c978047..dbb48e6 100755 --- a/src/wfuzz/externals/reqresp/TextParser.py +++ b/src/wfuzz/externals/reqresp/TextParser.py @@ -1,3 +1,5 @@ diff -Nru wfuzz-2.4/debian/patches/switch-to-python3.patch wfuzz-2.4.5/debian/patches/switch-to-python3.patch --- wfuzz-2.4/debian/patches/switch-to-python3.patch 2019-05-03 09:43:02.000000000 +0000 +++ wfuzz-2.4.5/debian/patches/switch-to-python3.patch 2020-02-06 10:34:44.000000000 +0000 @@ -1,9 +1,22 @@ -Description: Switch to Python3 - Change shebang to use python3 -Author: Sophie Brun +From: Sophie Brun +Date: Thu, 6 Feb 2020 11:34:12 +0100 +Subject: Switch to Python3 + +Last-Update: 2019-05-03 + +Change shebang to use python3 Last-Update: 2019-05-03 --- -This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ + src/wfencode.py | 2 +- + src/wfpayload.py | 2 +- + src/wfuzz-cli.py | 2 +- + src/wfuzz/wfuzz.py | 2 +- + src/wxfuzz.py | 2 +- + tests/test_acceptance.py | 2 +- + 6 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/src/wfencode.py b/src/wfencode.py +index 2576d08..f89375d 100755 --- a/src/wfencode.py +++ b/src/wfencode.py @@ -1,4 +1,4 @@ @@ -12,6 +25,8 @@ from wfuzz.wfuzz import main_encoder if __name__ == '__main__': +diff --git a/src/wfpayload.py b/src/wfpayload.py +index 0c9f429..66aafe4 100644 --- a/src/wfpayload.py +++ b/src/wfpayload.py @@ -1,4 +1,4 @@ @@ -20,6 +35,8 @@ from wfuzz.wfuzz import main_filter if __name__ == '__main__': +diff --git a/src/wfuzz-cli.py b/src/wfuzz-cli.py +index b96ef02..c3443da 100644 --- a/src/wfuzz-cli.py +++ b/src/wfuzz-cli.py @@ -1,4 +1,4 @@ @@ -28,6 +45,8 @@ from wfuzz.wfuzz import main +diff --git a/src/wfuzz/wfuzz.py b/src/wfuzz/wfuzz.py +index c36a4a4..b62d7a0 100644 --- a/src/wfuzz/wfuzz.py +++ b/src/wfuzz/wfuzz.py @@ -1,4 +1,4 @@ @@ -36,6 +55,8 @@ import sys from .core import Fuzzer +diff --git a/src/wxfuzz.py b/src/wxfuzz.py +index 86ae676..ec0b365 100644 --- a/src/wxfuzz.py +++ b/src/wxfuzz.py @@ -1,4 +1,4 @@ @@ -44,6 +65,8 @@ from wfuzz.wfuzz import main_gui +diff --git a/tests/test_acceptance.py b/tests/test_acceptance.py +index 9d3e03e..d1a4433 100644 --- a/tests/test_acceptance.py +++ b/tests/test_acceptance.py @@ -1,4 +1,4 @@ diff -Nru wfuzz-2.4/debian/patches/upgrade-doc-installation.patch wfuzz-2.4.5/debian/patches/upgrade-doc-installation.patch --- wfuzz-2.4/debian/patches/upgrade-doc-installation.patch 2019-05-03 09:43:02.000000000 +0000 +++ wfuzz-2.4.5/debian/patches/upgrade-doc-installation.patch 2020-02-06 10:34:44.000000000 +0000 @@ -1,9 +1,17 @@ -Description: don't install docs as data_files - By default the docs are installed into /usr/docs. -Author: Sophie Brun +From: Sophie Brun +Date: Thu, 6 Feb 2020 11:34:12 +0100 +Subject: don't install docs as data_files + +Last-Update: 2019-05-03 + +By default the docs are installed into /usr/docs. Last-Update: 2019-05-03 --- -This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ + setup.py | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/setup.py b/setup.py +index e357107..af45e0b 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,6 @@ if sys.platform.startswith("win"): diff -Nru wfuzz-2.4/debian/salsa-ci.yml wfuzz-2.4.5/debian/salsa-ci.yml --- wfuzz-2.4/debian/salsa-ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ wfuzz-2.4.5/debian/salsa-ci.yml 2020-02-06 10:34:44.000000000 +0000 @@ -0,0 +1,4 @@ +--- +include: + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml diff -Nru wfuzz-2.4/docs/user/advanced.rst wfuzz-2.4.5/docs/user/advanced.rst --- wfuzz-2.4/docs/user/advanced.rst 2019-04-27 09:17:17.000000000 +0000 +++ wfuzz-2.4.5/docs/user/advanced.rst 2020-01-23 13:31:48.000000000 +0000 @@ -508,7 +508,8 @@ headers.response.<> Specified HTTP response given header params.all All HTTP request GET and POST parameters params.get All HTTP request GET parameters -params.post All HTTP request POST parameters +params.post HTTP request POST parameters in returned as a dictionary +params.raw_post HTTP request POST parameters payload params.get.<> Spcified HTTP request GET parameter params.post.<> Spcified HTTP request POST parameter pstrip Returns a signature of the HTTP request using the parameter's names without values (useful for unique operations) @@ -516,7 +517,7 @@ reqtime Returns the total time that HTTP request took to be retrieved ============================ ============================================= -It is worth noting that Wfuzz will try to parse the POST parameters according to the specified content type header. Currently, application/x-www-form-urlencoded, multipart/form-dat and application/json are supported. +It is worth noting that Wfuzz will try to parse the POST parameters according to the specified content type header. Currently, application/x-www-form-urlencoded, multipart/form-dat and application/json are supported. This is prone to error depending on the data format, raw_post will not try to do any processing. FuzzRequest URL field is broken in smaller (read only) parts using the urlparse Python's module in the urlp attribute. diff -Nru wfuzz-2.4/docs/user/installation.rst wfuzz-2.4.5/docs/user/installation.rst --- wfuzz-2.4/docs/user/installation.rst 2019-04-27 09:17:17.000000000 +0000 +++ wfuzz-2.4.5/docs/user/installation.rst 2020-01-23 13:31:48.000000000 +0000 @@ -125,13 +125,16 @@ 2. mkdir ~/python-pycurl-openssl 3. cd ~/python-pycurl-openssl 4. sudo apt-get source python-pycurl -5. sudo apt-get build-dep python-pycurl -6. sudo apt-get install libcurl4-openssl-dev -7. sudo dpkg-source -x pycurl_7.19.0-3build1.dsc -8. cd pycurl-7.19.0 -9. edit debian/control file and replace all instances of “libcurl4-gnutls-dev” with “libcurl4-openssl-dev” -10. sudo PYCURL_SSL_LIBRARY=openssl dpkg-buildpackage -rfakeroot -b -11. sudo dpkg -i ../python-pycurl_7.19.0-3build1_i386.deb +5. sudo apt-get build-dep python-pycurl -y +6. sudo apt-get install libcurl4-openssl-dev -y +*** CAUTION: BE CAREFUL WITH THIS OR DELETE THE DIRECTORY MANUALLY TO BE SAFE *** +7. sudo rm -r ./*/ ; dpkg-source -x pycurl_7*.dsc # *** CAUTION: BE CAREFUL WITH THIS OR DELETE THE DIRECTORY MANUALLY TO BE SAFE *** +8. cd pycurl*/ +9. edit debian/control file and replace all instances of “libcurl4-gnutls-dev” with “libcurl4-openssl-dev”: +sed -i 's/libcurl4-gnutls-dev/libcurl4-openssl-dev/g' debian/control +sed -i 's/rm -f/rm -rf/g' debian/rules # fix debian/rules 'rm -r' typo preventing existing directory delete +10. sudo PYCURL_SSL_LIBRARY=openssl; dpkg-buildpackage -rfakeroot -b -uc -us +11. sudo dpkg -i ../python-pycurl_7*.deb If there is still the error:: diff -Nru wfuzz-2.4/.gitignore wfuzz-2.4.5/.gitignore --- wfuzz-2.4/.gitignore 2019-04-27 09:17:17.000000000 +0000 +++ wfuzz-2.4.5/.gitignore 2020-01-23 13:31:48.000000000 +0000 @@ -60,3 +60,6 @@ *.swo wfuzz.ini + +# Jetbrains IDE +.idea diff -Nru wfuzz-2.4/setup.py wfuzz-2.4.5/setup.py --- wfuzz-2.4/setup.py 2019-04-27 09:17:17.000000000 +0000 +++ wfuzz-2.4.5/setup.py 2020-01-23 13:31:48.000000000 +0000 @@ -20,7 +20,7 @@ ] install_requires = [ - 'pycurl', + 'pycurl<=7.43.0.3', 'pyparsing', 'future', 'six', @@ -69,5 +69,6 @@ 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', ), ) diff -Nru wfuzz-2.4/src/wfuzz/externals/reqresp/Request.py wfuzz-2.4.5/src/wfuzz/externals/reqresp/Request.py --- wfuzz-2.4/src/wfuzz/externals/reqresp/Request.py 2019-04-27 09:17:17.000000000 +0000 +++ wfuzz-2.4.5/src/wfuzz/externals/reqresp/Request.py 2020-01-23 13:31:48.000000000 +0000 @@ -50,7 +50,7 @@ self.multiPOSThead = {} self.__variablesGET = VariablesSet() - self.__variablesPOST = VariablesSet() + self._variablesPOST = VariablesSet() self._non_parsed_post = None # diccionario, por ejemplo headers["Cookie"] @@ -89,7 +89,7 @@ @property def method(self): if self._method is None: - return "POST" if (self.getPOSTVars() or self._non_parsed_post is not None) else "GET" + return "POST" if self._non_parsed_post is not None else "GET" return self._method @@ -148,17 +148,14 @@ elif name == "path": return self.__path elif name == "postdata": - if self._non_parsed_post is not None: - return self._non_parsed_post - if self.ContentType == "application/x-www-form-urlencoded": - return self.__variablesPOST.urlEncoded() + return self._variablesPOST.urlEncoded() elif self.ContentType == "multipart/form-data": - return self.__variablesPOST.multipartEncoded() + return self._variablesPOST.multipartEncoded() elif self.ContentType == 'application/json': - return self.__variablesPOST.json_encoded() + return self._variablesPOST.json_encoded() else: - return self.__variablesPOST.urlEncoded() + return self._variablesPOST.urlEncoded() else: raise AttributeError @@ -210,10 +207,10 @@ return self.__variablesGET.existsVar(key) def existPOSTVar(self, key): - return self.__variablesPOST.existsVar(key) + return self._variablesPOST.existsVar(key) def setVariablePOST(self, key, value): - v = self.__variablesPOST.getVariable(key) + v = self._variablesPOST.getVariable(key) v.update(value) # self._headers["Content-Length"] = str(len(self.postdata)) @@ -225,21 +222,25 @@ return self.__variablesGET.variables def getPOSTVars(self): - return self.__variablesPOST.variables + return self._variablesPOST.variables def setPostData(self, pd, boundary=None): + self._non_parsed_post = pd + self._variablesPOST = VariablesSet() + try: - self.__variablesPOST = VariablesSet() - if self.ContentType == "application/x-www-form-urlencoded": - self.__variablesPOST.parseUrlEncoded(pd) - elif self.ContentType == "multipart/form-data": - self.__variablesPOST.parseMultipart(pd, boundary) + if self.ContentType == "multipart/form-data": + self._variablesPOST.parseMultipart(pd, boundary) elif self.ContentType == 'application/json': - self.__variablesPOST.parse_json_encoded(pd) + self._variablesPOST.parse_json_encoded(pd) else: - self.__variablesPOST.parseUrlEncoded(pd) + self._variablesPOST.parseUrlEncoded(pd) except Exception: - self._non_parsed_post = pd + try: + self._variablesPOST.parseUrlEncoded(pd) + except Exception: + print("Warning: POST parameters not parsed") + pass ############################################################################ @@ -345,8 +346,8 @@ else: c.setopt(pycurl.CUSTOMREQUEST, req.method) - if req.getPOSTVars() or req._non_parsed_post is not None: - c.setopt(pycurl.POSTFIELDS, python2_3_convert_to_unicode(req.postdata)) + if req._non_parsed_post is not None: + c.setopt(pycurl.POSTFIELDS, python2_3_convert_to_unicode(req._non_parsed_post)) c.setopt(pycurl.FOLLOWLOCATION, 1 if req.followLocation else 0) @@ -393,7 +394,7 @@ # ######## ESTE conjunto de funciones no es necesario para el uso habitual de la clase def getAll(self): - pd = self.postdata + pd = self._non_parsed_post if self._non_parsed_post else '' string = str(self.method) + " " + str(self.pathWithVariables) + " " + str(self.protocol) + "\n" for i, j in self._headers.items(): string += i + ": " + j + "\n" @@ -421,7 +422,7 @@ tp = TextParser() tp.setSource("string", rawRequest) - self.__variablesPOST = VariablesSet() + self._variablesPOST = VariablesSet() self._headers = {} # diccionario, por ejemplo headers["Cookie"] tp.readLine() @@ -446,10 +447,17 @@ self.setUrl(prot + "://" + self._headers["Host"] + pathTMP) - pd = "" + # ignore CRLFs until request line + while tp.lastline == '' and tp.readLine(): + pass + # TODO: hacky, might need to change tp.readline returning read bytes instead + pd = "" + if tp.lastFull_line: + pd += tp.lastFull_line + while tp.readLine(): - pd += tp.lastline + pd += tp.lastFull_line if pd: boundary = None diff -Nru wfuzz-2.4/src/wfuzz/externals/reqresp/Response.py wfuzz-2.4.5/src/wfuzz/externals/reqresp/Response.py --- wfuzz-2.4/src/wfuzz/externals/reqresp/Response.py 2019-04-27 09:17:17.000000000 +0000 +++ wfuzz-2.4.5/src/wfuzz/externals/reqresp/Response.py 2020-01-23 13:31:48.000000000 +0000 @@ -175,7 +175,14 @@ else: self._headers = [] - # TODO: this might add to rawbody not directly to __content + # ignore CRLFs until request line + while tp.lastline == '' and tp.readLine(): + pass + + # TODO: this should be added to rawbody not directly to __content + if tp.lastFull_line: + self.addContent(tp.lastFull_line) + while tp.skip(1): self.addContent(tp.lastFull_line) diff -Nru wfuzz-2.4/src/wfuzz/externals/reqresp/Variables.py wfuzz-2.4.5/src/wfuzz/externals/reqresp/Variables.py --- wfuzz-2.4/src/wfuzz/externals/reqresp/Variables.py 2019-04-27 09:17:17.000000000 +0000 +++ wfuzz-2.4.5/src/wfuzz/externals/reqresp/Variables.py 2020-01-23 13:31:48.000000000 +0000 @@ -83,11 +83,11 @@ for i in cad.split("&"): if i: - list = i.split("=", 1) - if len(list) == 1: - dicc.append(Variable(list[0], None)) - elif len(list) == 2: - dicc.append(Variable(list[0], list[1])) + var_list = i.split("=", 1) + if len(var_list) == 1: + dicc.append(Variable(var_list[0], None)) + elif len(var_list) == 2: + dicc.append(Variable(var_list[0], var_list[1])) self.variables = dicc @@ -131,6 +131,6 @@ if value[-2:] == "\r\n": value = value[:-2] - dicc.append(Variable(var, value, headers)) + dicc.append(Variable(var, value.strip(), headers)) self.variables = dicc diff -Nru wfuzz-2.4/src/wfuzz/fuzzobjects.py wfuzz-2.4.5/src/wfuzz/fuzzobjects.py --- wfuzz-2.4/src/wfuzz/fuzzobjects.py 2019-04-27 09:17:17.000000000 +0000 +++ wfuzz-2.4.5/src/wfuzz/fuzzobjects.py 2020-01-23 13:31:48.000000000 +0000 @@ -18,7 +18,7 @@ from .filter import FuzzResFilter from .externals.reqresp import Request, Response -from .exception import FuzzExceptBadAPI, FuzzExceptBadOptions, FuzzExceptInternalError +from .exception import FuzzExceptBadAPI, FuzzExceptBadOptions, FuzzExceptInternalError, FuzzException from .facade import Facade, ERROR_CODE from .mixins import FuzzRequestUrlMixing, FuzzRequestSoupMixing @@ -114,20 +114,24 @@ @property def post(self): - if self._req._non_parsed_post is None: - return params.param([(x.name, x.value) for x in self._req.getPOSTVars()]) - else: - return self._req.postdata + return params.param([(x.name, x.value) for x in self._req.getPOSTVars()]) @post.setter def post(self, pp): if isinstance(pp, dict): for key, value in pp.items(): self._req.setVariablePOST(key, str(value) if value is not None else value) + + self._req._non_parsed_post = self._req._variablesPOST.urlEncoded() + elif isinstance(pp, str): self._req.setPostData(pp) @property + def raw_post(self): + return self._req._non_parsed_post + + @property def all(self): return params.param(self.get + self.post) @@ -330,12 +334,13 @@ self._request.parseRequest(raw, scheme) # Parse request sets postdata = '' when there's POST request without data - if self.method == "POST" and not self.params.post: - self.params.post = {'': None} + if self.method == "POST" and self.params.raw_post is None: + self.params.post = '' if raw_response: rp = Response() - raw_response = python2_3_convert_from_unicode(raw_response.decode("utf-8", errors='surrogateescape')) + if not isinstance(raw_response, str): + raw_response = python2_3_convert_from_unicode(raw_response.decode("utf-8", errors='surrogateescape')) rp.parseResponse(raw_response, raw_content) self._request.response = rp @@ -394,7 +399,7 @@ newreq.wf_ip = self.wf_ip newreq.headers.request = self.headers.request - newreq.params.post = self.params.post + newreq.params.post = self.params.raw_post newreq.follow = self.follow newreq.auth = self.auth diff -Nru wfuzz-2.4/src/wfuzz/__init__.py wfuzz-2.4.5/src/wfuzz/__init__.py --- wfuzz-2.4/src/wfuzz/__init__.py 2019-04-27 09:17:17.000000000 +0000 +++ wfuzz-2.4.5/src/wfuzz/__init__.py 2020-01-23 13:31:48.000000000 +0000 @@ -1,5 +1,5 @@ __title__ = 'wfuzz' -__version__ = "2.4" +__version__ = "2.4.5" __build__ = 0x023000 __author__ = 'Xavier Mendez' __license__ = 'GPL 2.0' diff -Nru wfuzz-2.4/src/wfuzz/plugins/payloads/burpitem.py wfuzz-2.4.5/src/wfuzz/plugins/payloads/burpitem.py --- wfuzz-2.4/src/wfuzz/plugins/payloads/burpitem.py 1970-01-01 00:00:00.000000000 +0000 +++ wfuzz-2.4.5/src/wfuzz/plugins/payloads/burpitem.py 2020-01-23 13:31:48.000000000 +0000 @@ -0,0 +1,64 @@ +from wfuzz.externals.moduleman.plugin import moduleman_plugin +from wfuzz.exception import FuzzExceptBadFile +from wfuzz.fuzzobjects import FuzzResult, FuzzRequest +from wfuzz.plugin_api.base import BasePayload +from wfuzz.utils import rgetattr +import xml.etree.cElementTree as ET +from base64 import b64decode + + +@moduleman_plugin +class burpitem(BasePayload): + name = "burpitem" + author = ("Bendegúz Nagy (@PaperTsar)",) + version = "0.1" + description = ( + "This payload loads request/response from items saved from Burpsuite.", + ) + summary = "This payload loads request/response from items saved from Burpsuite." + category = ["default"] + priority = 99 + + parameters = ( + ("fn", "", True, "Filename of a valid Burp item file."), + ("attr", None, False, "Attribute of fuzzresult to return. If not specified the whole object is returned."), + ) + + default_parameter = "fn" + + def __init__(self, params): + BasePayload.__init__(self, params) + + self.__max = -1 + self.attr = self.params["attr"] + self._it = self._gen_burpitem(self.params["fn"]) + + def __iter__(self): + return self + + def count(self): + return self.__max + + def __next__(self): + next_item = next(self._it) + + return next_item if not self.attr else rgetattr(next_item, self.attr) + + def _gen_burpitem(self, output_fn): + try: + tree = ET.parse(self.find_file(output_fn)) + for item in tree.getroot().iter('item'): + fr = FuzzRequest() + fr.update_from_raw_http(raw=b64decode(item.find('request').text or "").decode('utf-8'), + scheme=item.find('protocol').text, + raw_response=b64decode(item.find('response').text or "")) + fr.wf_ip = {'ip': item.find('host').attrib.get('ip', None) or item.find('host').text, + 'port': item.find('port').text} + frr = FuzzResult(history=fr) + + yield frr.update() + return + except IOError as e: + raise FuzzExceptBadFile("Error opening Burp items payload file. %s" % str(e)) + except EOFError: + return diff -Nru wfuzz-2.4/src/wfuzz/plugins/payloads/wfuzzp.py wfuzz-2.4.5/src/wfuzz/plugins/payloads/wfuzzp.py --- wfuzz-2.4/src/wfuzz/plugins/payloads/wfuzzp.py 2019-04-27 09:17:17.000000000 +0000 +++ wfuzz-2.4.5/src/wfuzz/plugins/payloads/wfuzzp.py 2020-01-23 13:31:48.000000000 +0000 @@ -60,4 +60,4 @@ except IOError as e: raise FuzzExceptBadFile("Error opening wfuzz payload file. %s" % str(e)) except EOFError: - raise StopIteration + return diff -Nru wfuzz-2.4/src/wfuzz/ui/console/output.py wfuzz-2.4.5/src/wfuzz/ui/console/output.py --- wfuzz-2.4/src/wfuzz/ui/console/output.py 2019-04-27 09:17:17.000000000 +0000 +++ wfuzz-2.4.5/src/wfuzz/ui/console/output.py 2020-01-23 13:31:48.000000000 +0000 @@ -151,4 +151,6 @@ cr = (os.environ.get('LINES'), os.environ.get('COLUMNS')) except Exception: return None + if not cr[0]: + return None return int(cr[1]), int(cr[0]) diff -Nru wfuzz-2.4/tests/test_acceptance.py wfuzz-2.4.5/tests/test_acceptance.py --- wfuzz-2.4/tests/test_acceptance.py 2019-04-27 09:17:17.000000000 +0000 +++ wfuzz-2.4.5/tests/test_acceptance.py 2020-01-23 13:31:48.000000000 +0000 @@ -49,6 +49,8 @@ ("test_novalue_post_fuzz", "-z list --zD a -u {}/anything -d FUZZ".format(HTTPBIN_URL), "-z wfuzzp --zD $$PREVFILE$$ -u FUZZ --filter r.params.post.a:=1 --field r.params.post.a", ["1"], None), ("test_json_post_fuzz2", "-z list --zD anything -u {}/FUZZ -d {{\"a\":\"2\"}} -H Content-Type:application/json".format(HTTPBIN_URL), "-z wfuzzp --zD $$PREVFILE$$ -u FUZZ --field r.params.post.a", ["2"], None), ("test_json_post_fuzz3", "-z list --zD anything -u {}/FUZZ -d {{\"a\":\"2\"}} -H Content-Type:application/json".format(HTTPBIN_URL), "-z wfuzzp --zD $$PREVFILE$$ -u FUZZ --filter r.params.post.a:=1 --field r.params.post.a", ["1"], None), + ("test_json_nested", "-z list --zD anything -u {}/FUZZ -d {{\"test\":\"me\",\"another\":1,\"nested\":{{\"this\":2}}}} -H Content-Type:application/json".format(HTTPBIN_URL), "-z wfuzzp --zD $$PREVFILE$$ -u FUZZ --field r.params.post.nested.this", [2], None), + ("test_json_nested2", "-z list --zD anything -u {}/FUZZ -d {{\"test\":\"me\",\"another\":1,\"nested\":{{\"this\":2}}}} -H Content-Type:application/json".format(HTTPBIN_URL), "-z wfuzzp --zD $$PREVFILE$$ -u FUZZ --field r.params.post.another", [1], None), # field fuzz values ("test_desc_fuzz", "-z range,1-1 {}/FUZZ".format(HTTPBIN_URL), "-z wfuzzp,$$PREVFILE$$ FUZZ", ["http://localhost:9000/1"], None), @@ -443,7 +445,7 @@ def create_savedsession_tests(test_list, test_gen_fun): """ - generates wfuzz tests that run 2 times with recipe input, expecting same results. + generates wfuzz tests that run 2 times with a saved session, expecting same results. """ for test_name, prev_cli, next_cli, expected_res, exception_str in test_list: diff -Nru wfuzz-2.4/tests/test_req_parse.py wfuzz-2.4.5/tests/test_req_parse.py --- wfuzz-2.4/tests/test_req_parse.py 1970-01-01 00:00:00.000000000 +0000 +++ wfuzz-2.4.5/tests/test_req_parse.py 2020-01-23 13:31:48.000000000 +0000 @@ -0,0 +1,160 @@ +import unittest + +from wfuzz.fuzzobjects import FuzzRequest + + +http_post_request = '''POST /slipstream/view HTTP/1.1 +Host: www +User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0 +Accept: */* +Accept-Language: en-GB,en;q=0.5 +Accept-Encoding: gzip, deflate +Referer: https://www +Content-Type: text/plain;charset=UTF-8 +Origin: https://www +Content-Length: 3387 +Connection: close + + + +a=1''' + + +http_get_request = '''GET /sttc/bpk-fonts/55b577a1.woff2 HTTP/1.1 +Host: js.skyscnr.com +User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0 +Accept: application/font-woff2;q=1.0,application/font-woff;q=0.9,*/*;q=0.8 +Accept-Language: en-GB,en;q=0.5 +Accept-Encoding: gzip, deflate +Origin: https://www.skyscanner.es +Connection: close +Referer: https://js.skyscnr.com/sttc/oc-registry/components/base-stylesheet/0.1.33/build//static/css/main.e09b44e2.css + + +''' + +http_response = '''HTTP/1.1 201 Created +Content-Type: application/json +Content-Length: 51 +Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0 +Expires: -1 +Last-Modified: Mon, 30 Dec 2019 13:16:57 GMT +Pragma: no-cache +Server: Unspecified +Date: Mon, 30 Dec 2019 13:16:57 GMT +Connection: close + +LINE_1''' + +http_response_no_content = '''HTTP/1.1 201 Created +Content-Type: application/json +Content-Length: 51 +Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0 +Expires: -1 +Last-Modified: Mon, 30 Dec 2019 13:16:57 GMT +Pragma: no-cache +Server: Unspecified +Date: Mon, 30 Dec 2019 13:16:57 GMT +Connection: close +''' + +http_multi_request = '''POST /tr/ HTTP/1.1 +Host: www.facebook.com +User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0 +Accept: */* +Accept-Language: en-GB,en;q=0.5 +Accept-Encoding: gzip, deflate +Content-Type: multipart/form-data; boundary=---------------------------18698393981150719881279620016 +Content-Length: 3320 +Origin: https://www.skyscanner.es +Connection: close +Referer: https://www.skyscanner.es/ + +-----------------------------18698393981150719881279620016 +Content-Disposition: form-data; name="id" + +561358470665569 +-----------------------------18698393981150719881279620016 +Content-Disposition: form-data; name="rqm" + +SB +-----------------------------18698393981150719881279620016-- + +''' + +http_follow_response = '''HTTP/1.1 301 Moved Permanently +Location: http://www.google.com/ +Content-Type: text/html; charset=UTF-8 +Date: Mon, 30 Dec 2019 20:26:23 GMT +Expires: Wed, 29 Jan 2020 20:26:23 GMT +Cache-Control: public, max-age=2592000 +Server: gws +Content-Length: 219 +X-XSS-Protection: 0 +X-Frame-Options: SAMEORIGIN + +HTTP/1.1 200 OK +Date: Mon, 30 Dec 2019 20:26:23 GMT +Expires: -1 +Cache-Control: private, max-age=0 +Content-Type: text/html; charset=ISO-8859-1 +P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info." +Server: gws +X-XSS-Protection: 0 +X-Frame-Options: SAMEORIGIN +Set-Cookie: 1P_JAR=2019-12-30-20; expires=Wed, 29-Jan-2020 20:26:23 GMT; path=/; domain=.google.com +Set-Cookie: NID=194=Tygb5SRWSRvznMKZn4Dnl0SIkI9zcjk_U9OnBb9RlhyXWKlvEJSCorghYsp5IPR-bAm31XZlKGiL0RjLxjGigGqkGguTVmJ1C4Ae6JUKLoAYLbR-C8xAvuwoXm6Nw61Wep9U1zkq6evNZ-WbKyfYvOS6WrUi_3TXU7BqUaWZsJY; expires=Tue, 30-Jun-2020 20:26:23 GMT; path=/; domain=.google.com; HttpOnly +Accept-Ranges: none +Vary: Accept-Encoding +Transfer-Encoding: chunked + +LINE_1''' + + +class ParseRequestTest(unittest.TestCase): + def __init__(self, *args, **kwargs): + super(ParseRequestTest, self).__init__(*args, **kwargs) + self.maxDiff = 1000 + + def test_2_ways_of_parsing_content(self): + fr = FuzzRequest() + fr.update_from_raw_http(http_multi_request, "https", http_response) + + fr2 = FuzzRequest() + fr2.update_from_raw_http(http_multi_request, "https", http_response_no_content, b"LINE_1") + + # raw content takes precedence + fr3 = FuzzRequest() + fr3.update_from_raw_http(http_multi_request, "https", http_response, b"LINE_0") + + self.assertEqual(fr.content, fr2.content) + self.assertEqual(fr3.content, "LINE_0") + + def test_parse_multi_raw_request(self): + fr = FuzzRequest() + fr.update_from_raw_http(http_multi_request, "https", http_response) + + self.assertEqual(fr.params.post.id, "561358470665569") + self.assertEqual(fr.params.post.rqm, "SB") + self.assertEqual(fr.content, "LINE_1") + + def test_parse_raw_multi_response(self): + fr = FuzzRequest() + fr.update_from_raw_http(http_multi_request, "https", http_follow_response) + + self.assertEqual(fr.content, "LINE_1") + self.assertEqual(fr.code, 200) + + def test_parse_get_crlf_request(self): + fr = FuzzRequest() + fr.update_from_raw_http(http_get_request, "https", "\n\n\n") + + self.assertEqual(fr.method, "GET") + self.assertEqual(fr.params.raw_post, None) + + def test_parse_crlf_post_request(self): + fr = FuzzRequest() + fr.update_from_raw_http(http_post_request, "https", "\n\n\n") + + self.assertEqual(fr.method, "POST") + self.assertEqual(fr.params.post, {'a': '1'}) diff -Nru wfuzz-2.4/tests/test_reqresp.py wfuzz-2.4.5/tests/test_reqresp.py --- wfuzz-2.4/tests/test_reqresp.py 2019-04-27 09:17:17.000000000 +0000 +++ wfuzz-2.4.5/tests/test_reqresp.py 2020-01-23 13:31:48.000000000 +0000 @@ -118,42 +118,56 @@ fr.url = "www.wfuzz.org" self.assertEqual(fr.host, "www.wfuzz.org") - def test_setpostdata(self): + def test_empy_post(self): fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.post = 'a=1' + fr.params.post = '' self.assertEqual(fr.method, "POST") - self.assertEqual(fr.params.post, {'a': '1'}) + self.assertEqual(fr.params.post, {'': None}) + self.assertEqual(fr.params.raw_post, '') fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.post = '1' + fr.params.post = {} self.assertEqual(fr.method, "POST") - self.assertEqual(fr.params.post, {'1': None}) + self.assertEqual(fr.params.post, {}) + self.assertEqual(fr.params.raw_post, '') fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.post = '' + fr.params.post = None + self.assertEqual(fr.method, "GET") + self.assertEqual(fr.params.post, {}) + self.assertEqual(fr.params.raw_post, None) + + def test_setpostdata(self): + fr = FuzzRequest() + fr.url = "http://www.wfuzz.org/" + fr.params.post = 'a=1' self.assertEqual(fr.method, "POST") - self.assertEqual(fr.params.post, {'': None}) + self.assertEqual(fr.params.raw_post, 'a=1') + self.assertEqual(fr.params.post, {'a': '1'}) fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" - fr.params.post = {} - self.assertEqual(fr.method, "GET") - self.assertEqual(fr.params.post, {}) + fr.params.post = '1' + self.assertEqual(fr.method, "POST") + self.assertEqual(fr.params.post, {'1': None}) + self.assertEqual(fr.params.raw_post, '1') fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" fr.params.post = {'a': 1} self.assertEqual(fr.method, "POST") self.assertEqual(fr.params.post, {'a': '1'}) + self.assertEqual(fr.params.raw_post, 'a=1') fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" fr.params.post = {'a': '1'} self.assertEqual(fr.method, "POST") self.assertEqual(fr.params.post, {'a': '1'}) + self.assertEqual(fr.params.raw_post, 'a=1') fr = FuzzRequest() fr.url = "http://www.wfuzz.org/" @@ -161,13 +175,6 @@ self.assertEqual(fr.method, "POST") self.assertEqual(fr.params.post, {"{'a': '1'}": None}) - fr = FuzzRequest() - fr.url = "http://www.wfuzz.org/" - fr.params.post = '1' - fr.headers.request = {'Content-Type': 'application/json'} - self.assertEqual(fr.method, "POST") - self.assertEqual(fr.params.post, {'1': None}) - def test_setgetdata(self): fr = FuzzRequest() @@ -237,6 +244,72 @@ fr.params.post = '' self.assertEqual(fr.to_cache_key(), 'http://www.wfuzz.org/-p') + def test_cache_key_json_header_before(self): + fr = FuzzRequest() + fr.url = "http://www.wfuzz.org/" + fr.params.post = '1' + fr.headers.request = {'Content-Type': 'application/json'} + + self.assertEqual(fr.to_cache_key(), 'http://www.wfuzz.org/-p1') + + def test_cache_key_json_header_after(self): + fr = FuzzRequest() + fr.headers.request = {'Content-Type': 'application/json'} + fr.url = "http://www.wfuzz.org/" + fr.params.post = '1' + + self.assertEqual(fr.to_cache_key(), 'http://www.wfuzz.org/-p1') + + def test_cache_key_get_var(self): + fr = FuzzRequest() + fr.url = "http://www.wfuzz.org/?a&b=1" + + self.assertEqual(fr.to_cache_key(), 'http://www.wfuzz.org/-ga-gb') + + def test_get_vars(self): + fr = FuzzRequest() + fr.url = "http://www.wfuzz.org/?a&b=1" + self.assertEqual(fr.params.get, {'a': None, 'b': '1'}) + + fr = FuzzRequest() + fr.url = "http://www.wfuzz.org/?" + self.assertEqual(fr.params.get, {}) + + fr = FuzzRequest() + fr.url = "http://www.wfuzz.org/" + self.assertEqual(fr.params.get, {}) + + def test_setpostdata_with_json(self): + fr = FuzzRequest() + fr.headers.request = {'Content-Type': 'application/json'} + fr.url = "http://www.wfuzz.org/" + fr.params.post = '{"string": "Foo bar","boolean": false}' + self.assertEqual(fr.params.post, {'string': 'Foo bar', 'boolean': False}) + + fr = FuzzRequest() + fr.headers.request = {'Content-Type': 'application/json'} + fr.url = "http://www.wfuzz.org/" + fr.params.post = '{"array": [1,2]}' + self.assertEqual(fr.params.post, {'array': [1, 2]}) + + def test_post_bad_json(self): + fr = FuzzRequest() + fr.headers.request = {'Content-Type': 'application/json'} + fr.url = "http://www.wfuzz.org/" + fr.params.post = '1' + + self.assertEqual(fr.method, "POST") + self.assertEqual(fr.params.post, {'1': None}) + self.assertEqual(fr.params.raw_post, '1') + + fr = FuzzRequest() + fr.url = "http://www.wfuzz.org/" + fr.headers.request = {'Content-Type': 'application/json'} + fr.params.post = 'a=1' + self.assertEqual(fr.method, "POST") + self.assertEqual(fr.params.raw_post, "a=1") + self.assertEqual(fr.params.post, {'a': '1'}) + if __name__ == '__main__': unittest.main() diff -Nru wfuzz-2.4/tox.ini wfuzz-2.4.5/tox.ini --- wfuzz-2.4/tox.ini 2019-04-27 09:17:17.000000000 +0000 +++ wfuzz-2.4.5/tox.ini 2020-01-23 13:31:48.000000000 +0000 @@ -1,5 +1,5 @@ [tox] -envlist = begin,docker,py27,py36,end +envlist = begin,docker,py27,py36,py37,end [testenv] commands = diff -Nru wfuzz-2.4/.travis.yml wfuzz-2.4.5/.travis.yml --- wfuzz-2.4/.travis.yml 2019-04-27 09:17:17.000000000 +0000 +++ wfuzz-2.4.5/.travis.yml 2020-01-23 13:31:48.000000000 +0000 @@ -7,6 +7,7 @@ - "3.4" - "3.5" - "3.6" + - "3.7" before_install: - docker-compose -f tests/server_dir/docker-compose.yml up -d install: @@ -32,3 +33,7 @@ - /^v.*$/ tags: true python: 3.6 +addons: + apt: + packages: + - libcurl4-openssl-dev