Source code for ptp.tools.dirbuster.parser
"""
:synopsis: Specialized :class:`ptp.libptp.parser.AbstractParser` classes for the tool DirBuster.
.. moduleauthor:: Tao Sauvage
"""
import re
from ptp.libptp.parser import LineParser
from ptp.tools.dirbuster.signatures import DIRECTORIES, FILES
[docs]class DirbusterParser(LineParser):
"""DirBuster specialized parser."""
__tool__ = 'dirbuster'
__format__ = 'dirbuster'
__version__ = ['1.0-RC1']
#: :class:`str` -- Regex matching DirBuster section separator.
_re_sep = r"^-*$"
#: :class:`str` -- Regex matching DirBuster version.
_re_version = r"^DirBuster (?P<version>[0-9]+\.[0-9]+(-RC[0-9])?) - Report$"
#: :class:`str` -- Regex matching DirBuster directories status code.
_re_dir_status = r"^Dirs found with a (?P<status>[0-9]{3}) response:$"
#: :class:`str` -- Regex matching DirBuster files status code.
_re_file_status = r"^Files found with a (?P<status>[0-9]{3}) responce:$"
[docs] def __init__(self, pathname, filename='DirBuster-Report*', light=False, first=True):
"""Initialize DirbusterParser.
:param str pathname: Path to the report directory.
:param str filename: Regex matching the report file.
:param bool first: Only process first file (``True``) or each file that matched (``False``).
"""
LineParser.__init__(self, pathname, filename, first=first)
[docs] @classmethod
def is_mine(cls, pathname, filename='DirBuster-Report*', light=False, first=True):
"""Check if it can handle the report file.
:param str pathname: Path to the report directory.
:param str filename: Regex matching the report file.
:param bool light: `True` to only parse the ranking of the findings from the report.
:param bool first: Only process first file (``True``) or each file that matched (``False``).
:raises IOError: when the report file cannot be found.
:raises OSError: when the report file cannot be found.
:return: `True` if it supports the report, `False` otherwise.
:rtype: :class:`bool`
"""
stream = cls.handle_file(pathname, filename, first=first)
if stream and re.match(cls._re_version, stream[0]):
return True
return False
[docs] def parse_report(self):
"""Parser the results of a DirBuster report.
:return: List of dicts where each one represents a vuln.
:rtype: :class:`list`
"""
# Retrieve the results from the report.
discoveries = {'directories': [], 'files': [], 'errors': []}
type_disco = 'errors'
status_code = -1
for line in self.stream:
match_dir_status = re.match(self._re_dir_status, line)
match_file_status = re.match(self._re_file_status, line)
if match_dir_status:
type_disco = 'directories'
status_code = match_dir_status.group('status')
elif match_file_status:
type_disco = 'files'
status_code = match_file_status.group('status')
elif line.startswith('/'): # This line contains a discovery
status = int(status_code)
# If this is the section of the successful ones (2xx).
if status >= 200 and status < 300:
discoveries[type_disco].append(line)
# Match found discoveries against signatures database.
vulns = []
matching = ((DIRECTORIES, 'directories'), (FILES, 'files'))
for signatures, type_disco in matching:
try:
signatures = signatures.iteritems()
except AttributeError: # Python3
signatures = signatures.items()
for signature, ranking in signatures:
matched = [True for disco in discoveries[type_disco] if re.match(signature, disco)]
if True in matched:
vulns.extend([{'ranking': ranking}])
self.vulns = vulns
return vulns