Ejemplo n.º 1
0
    def setup(self):
        with self._plugin_lock:
            if self._signature_re is not None:
                return

            signatures = self._read_signatures()
            self._signature_re = multi_re(signatures, hint_len=2)
Ejemplo n.º 2
0
    def setup(self):
        with self._plugin_lock:
            if self._signature_re is not None:
                return

            signatures = self._read_signatures()
            self._signature_re = multi_re(signatures, hint_len=2)
Ejemplo n.º 3
0
    def setup(self):
        """
        :return: None, the result is saved in self._path_disc_regex_list
        """
        if self._signature_re is not None:
            return

        all_signatures = []

        for path_disclosure_string in get_common_directories():
            regex_string = '(%s.*?)[^A-Za-z0-9\._\-\\/\+~]'
            regex_string = regex_string % path_disclosure_string
            all_signatures.append(regex_string)
            
        self._signature_re = multi_re(all_signatures, hint_len=1)
Ejemplo n.º 4
0
    def setup(self):
        """
        :return: None, the result is saved in self._path_disc_regex_list
        """
        if self._signature_re is not None:
            return

        all_signatures = []

        for path_disclosure_string in get_common_directories():
            regex_string = '(%s.*?)[^A-Za-z0-9\._\-\\/\+~]'
            regex_string = regex_string % path_disclosure_string
            all_signatures.append(regex_string)
            
        self._signature_re = multi_re(all_signatures, hint_len=1)
Ejemplo n.º 5
0
    ('<asp:.*?%>', 'ASPX'),
    ('<jsp:.*?>', 'JSP'),

    ('<%! .*%>', 'JSP'),
    ('<%!\n.*%>', 'JSP'),
    ('<%!\r.*%>', 'JSP'),
    ('<%=.*%>', 'JSP or PHP'),

    ('<!--\s*%.*?%(--)?>', 'PHP'),
    ('<!--\s*\?.*?\?(--)?>', 'ASP or JSP'),
    ('<!--\s*jsp:.*?(--)?>', 'JSP'),
)

BLACKLIST = {'xml', 'xpacket'}

_multi_re = multi_re(SOURCE_CODE, re.IGNORECASE | re.DOTALL, hint_len=2)


def is_source_file(file_content):
    """
    :param file_content: The content of the http response body to analyze
    :return: A tuple with:
                - re.match object if the file_content matches a source code file
                - string with the source code programming language
    """
    for match, _, _, lang in _multi_re.query(file_content):

        match_str = match.group(0)

        for blacklist_str in BLACKLIST:
            if blacklist_str in match_str:
Ejemplo n.º 6
0
    ('<%@\n.*?%>', 'ASPX'),
    ('<%@\r.*?%>', 'ASPX'),
    ('<asp:.*?%>', 'ASPX'),
    ('<jsp:.*?>', 'JSP'),
    ('<%! .*%>', 'JSP'),
    ('<%!\n.*%>', 'JSP'),
    ('<%!\r.*%>', 'JSP'),
    ('<%=.*%>', 'JSP or PHP'),
    ('<!--\s*%.*?%(--)?>', 'PHP'),
    ('<!--\s*\?.*?\?(--)?>', 'ASP or JSP'),
    ('<!--\s*jsp:.*?(--)?>', 'JSP'),
)

BLACKLIST = {'xml', 'xpacket'}

_multi_re = multi_re(SOURCE_CODE, re.IGNORECASE | re.DOTALL, hint_len=2)


def is_source_file(file_content):
    """
    :param file_content: The content of the http response body to analyze
    :return: A tuple with:
                - re.match object if the file_content matches a source code file
                - string with the source code programming language
    """
    for match, _, _, lang in _multi_re.query(file_content):

        match_str = match.group(0)

        for blacklist_str in BLACKLIST:
            if blacklist_str in match_str:
Ejemplo n.º 7
0
class error_pages(GrepPlugin):
    """
    Grep every page for error pages.

    :author: Andres Riancho ([email protected])
    """

    ERROR_PAGES = (
        '<H1>Error page exception</H1>',

        # This signature fires up also in default 404 pages of aspx which
        # generates a lot of noise, so ... disabling it
        # '<span><H1>Server Error in ',
        '<h2> <i>Runtime Error</i> </h2></span>',
        '<h2> <i>Access is denied</i> </h2></span>',
        '<H3>Original Exception: </H3>',
        'Server object error',
        'invalid literal for int()',
        'exceptions.ValueError',
        '<font face="Arial" size=2>Type mismatch: ',
        '[an error occurred while processing this directive]',
        '<HTML><HEAD><TITLE>Error Occurred While Processing Request</TITLE>'
        '</HEAD><BODY><HR><H3>Error Occurred While Processing Request</H3><P>',

        # VBScript
        '<p>Microsoft VBScript runtime </font>',
        "<font face=\"Arial\" size=2>error '800a000d'</font>",

        # nwwcgi errors
        '<TITLE>nwwcgi Error',

        # ASP error I found during a pentest, the ASP used a foxpro db, not a
        # SQL injection
        '<font face="Arial" size=2>error \'800a0005\'</font>',
        '<h2> <i>Runtime Error</i> </h2></span>',
        # Some error in ASP when using COM objects.
        'Operation is not allowed when the object is closed.',
        # An error when ASP tries to include something and it fails
        '<p>Active Server Pages</font> <font face="Arial" size=2>error \'ASP 0126\'</font>',

        # ASPX
        '<b> Description: </b>An unhandled exception occurred during the execution of the'
        ' current web request',

        # Struts
        '] does not contain handler parameter named',

        # PHP
        '<b>Warning</b>: ',
        'No row with the given identifier',
        'open_basedir restriction in effect',
        "eval()'d code</b> on line <b>",
        "Cannot execute a blank command in",
        "Fatal error</b>:  preg_replace",
        "thrown in <b>",
        "#0 {main}",
        "Stack trace:",
        "</b> on line <b>",

        # python
        "PythonHandler django.core.handlers.modpython",
        "t = loader.get_template(template_name) # You need to create a 404.html template.",
        '<h2>Traceback <span>(innermost last)</span></h2>',

        # Java
        '[java.lang.',
        'class java.lang.',
        'java.lang.NullPointerException',
        'java.rmi.ServerException',
        'at java.lang.',
        'onclick="toggle(\'full exception chain stacktrace\')"',
        'at org.apache.catalina',
        'at org.apache.coyote.',
        'at org.apache.tomcat.',
        'at org.apache.jasper.',

        # https://github.com/andresriancho/w3af/issues/4001
        '<html><head><title>Application Exception</title>',

        # ruby
        '<h1 class="error_title">Ruby on Rails application could not be started</h1>',

        # Coldfusion
        '<title>Error Occurred While Processing Request</title></head><body><p></p>',
        '<HTML><HEAD><TITLE>Error Occurred While Processing Request</TITLE></HEAD><BODY><HR><H3>',
        '<TR><TD><H4>Error Diagnostic Information</H4><P><P>',
        '<li>Search the <a href="http://www.macromedia.com/support/coldfusion/" '
        'target="new">Knowledge Base</a> to find a solution to your problem.</li>',

        # http://www.programacion.net/asp/articulo/kbr_execute/
        'Server.Execute Error',

        # IIS
        '<h2 style="font:8pt/11pt verdana; color:000000">HTTP 403.6 - Forbidden: IP address rejected<br>',
        '<TITLE>500 Internal Server Error</TITLE>',
    )
    _multi_in = multi_in(ERROR_PAGES)

    VERSION_REGEX = (
        ('<address>(.*?)</address>', 'Apache'),
        ('<HR size="1" noshade="noshade"><h3>(.*?)</h3></body>',
         'Apache Tomcat'),
        ('<a href="http://www.microsoft.com/ContentRedirect.asp\?prd=iis&sbp=&pver=(.*?)&pid=&ID',
         'IIS'),

        # <b>Version Information:</b>&nbsp;Microsoft .NET Framework Version:1.1.4322.2300; ASP.NET Version:1.1.4322.2300
        ('<b>Version Information:</b>&nbsp;(.*?)\n', 'ASP .NET'))
    _multi_re = multi_re(VERSION_REGEX)

    MAX_REPORTED_PER_MSG = 10

    def __init__(self):
        GrepPlugin.__init__(self)

        #   Internal variables
        self._potential_vulns = DiskList(table_prefix='error_pages')

        self._already_reported_max_msg_exceeded = []
        self._already_reported_versions = []
        self._compiled_regex = []

    def grep(self, request, response):
        """
        Plugin entry point, find the error pages and report them.

        :param request: The HTTP request object.
        :param response: The HTTP response object
        :return: None
        """
        if not response.is_text_or_html():
            return

        self.find_error_page(request, response)
        self.find_version_numbers(request, response)

    def find_error_page(self, request, response):
        # There is no need to report more than one info for the
        # same result, the user will read the info object and
        # analyze it even if we report it only once. If we report
        # it twice, he'll get mad ;)
        for _, _, _, url, _ in self._potential_vulns:
            if url == response.get_url():
                return

        for msg in self._multi_in.query(response.body):
            if self._avoid_report(request, response, msg):
                continue

            # We found a new error in a response!
            desc = 'The URL: "%s" contains the descriptive error: "%s".'
            desc %= (response.get_url(), msg)

            title = 'Descriptive error page'

            data = (title, desc, response.id, response.get_url(), msg)
            self._potential_vulns.append(data)

            # Just report one instance for each HTTP response, no
            # matter if multiple strings match
            break

    def _avoid_report(self, request, response, msg):
        # We should avoid multiple reports for the same error message
        # the idea here is that the root cause for the same error
        # message might be the same, and fixing one will fix all.
        #
        # So the user receives the first report with MAX_REPORTED_PER_MSG
        # vulnerabilities, fixes the root cause, scans again and then
        # all those instances go away.
        #
        # Without this code, the scanner will potentially report
        # thousands of issues for the same error message. Which will
        # overwhelm the user.
        count = 0

        for title, desc, _id, url, highlight in self._potential_vulns:
            if highlight == msg:
                count += 1

        if count < self.MAX_REPORTED_PER_MSG:
            return False

        if msg not in self._already_reported_max_msg_exceeded:
            self._already_reported_max_msg_exceeded.append(msg)

            desc = ('The application returned multiple HTTP responses'
                    ' containing detailed error pages containing exceptions'
                    ' and internal information. The maximum number of'
                    ' vulnerabilities for this issue type was reached'
                    ' and no more issues will be reported.')

            i = Info('Multiple descriptive error pages', desc, [],
                     self.get_name())
            self.kb_append_uniq(self, 'error_page', i)

        return True

    def end(self):
        """
        This method is called when the plugin wont be used anymore.
        """
        all_findings = kb.kb.get_all_findings()

        for title, desc, _id, url, highlight in self._potential_vulns:
            for info in all_findings:
                # This makes sure that if the sqli plugin found a vulnerability
                # in the same URL as we found a detailed error, we won't report
                # the detailed error.
                #
                # If the user fixes the sqli vulnerability and runs the scan again
                # most likely the detailed error will disappear too. If the sqli
                # vulnerability disappears and this one remains, it will appear
                # as a new vulnerability in the second scan.
                if info.get_url() == url:
                    break
            else:
                i = Info(title, desc, _id, self.get_name())
                i.set_url(url)
                i.add_to_highlight(highlight)

                self.kb_append_uniq(self, 'error_page', i)

        self._potential_vulns.cleanup()

    def find_version_numbers(self, request, response):
        """
        Now i'll check if I can get a version number from the error page
        This is common in apache, tomcat, etc...
        """
        if 400 < response.get_code() < 600:

            for match, _, _, server in self._multi_re.query(response.body):
                match_string = match.group(0)
                if match_string not in self._already_reported_versions:
                    # Save the info obj
                    desc = 'An error page sent this %s version: "%s".'
                    desc %= (server, match_string)

                    i = Info('Error page with information disclosure', desc,
                             response.id, self.get_name())
                    i.set_url(response.get_url())
                    i.add_to_highlight(server)
                    i.add_to_highlight(match_string)

                    kb.kb.append(self, 'server', i)
                    kb.kb.raw_write(self, 'server', match_string)

                    self._already_reported_versions.append(match_string)

    def get_long_desc(self):
        """
        :return: A DETAILED description of the plugin functions and features.
        """
        return """
Ejemplo n.º 8
0
class sqli(AuditPlugin):
    """
    Find SQL injection bugs.
    :author: Andres Riancho ([email protected])
    """
    SQL_ERRORS_STR = (
        # ASP / MSSQL
        (r'System.Data.OleDb.OleDbException', dbms.MSSQL),
        (r'[SQL Server]', dbms.MSSQL),
        (r'[Microsoft][ODBC SQL Server Driver]', dbms.MSSQL),
        (r'[SQLServer JDBC Driver]', dbms.MSSQL),
        (r'[SqlException', dbms.MSSQL),
        (r'System.Data.SqlClient.SqlException', dbms.MSSQL),
        (r'Unclosed quotation mark after the character string', dbms.MSSQL),
        (r"'80040e14'", dbms.MSSQL),
        (r'mssql_query()', dbms.MSSQL),
        (r'odbc_exec()', dbms.MSSQL),
        (r'Microsoft OLE DB Provider for ODBC Drivers', dbms.MSSQL),
        (r'Microsoft OLE DB Provider for SQL Server', dbms.MSSQL),
        (r'Incorrect syntax near', dbms.MSSQL),
        (r'Sintaxis incorrecta cerca de', dbms.MSSQL),
        (r'Syntax error in string in query expression', dbms.MSSQL),
        (r'ADODB.Field (0x800A0BCD)<br>', dbms.MSSQL),
        (r"ADODB.Recordset'", dbms.MSSQL),
        (r"Unclosed quotation mark before the character string", dbms.MSSQL),
        (r"'80040e07'", dbms.MSSQL),
        (r'Microsoft SQL Native Client error', dbms.MSSQL),
        # DB2
        (r'SQLCODE', dbms.DB2),
        (r'DB2 SQL error:', dbms.DB2),
        (r'SQLSTATE', dbms.DB2),
        (r'[CLI Driver]', dbms.DB2),
        (r'[DB2/6000]', dbms.DB2),
        # Sybase
        (r"Sybase message:", dbms.SYBASE),
        (r"Sybase Driver", dbms.SYBASE),
        (r"[SYBASE]", dbms.SYBASE),
        # Access
        (r'Syntax error in query expression', dbms.ACCESS),
        (r'Data type mismatch in criteria expression.', dbms.ACCESS),
        (r'Microsoft JET Database Engine', dbms.ACCESS),
        (r'[Microsoft][ODBC Microsoft Access Driver]', dbms.ACCESS),
        # ORACLE
        (r'Microsoft OLE DB Provider for Oracle', dbms.ORACLE),
        (r'wrong number or types', dbms.ORACLE),
        # POSTGRE
        (r'PostgreSQL query failed:', dbms.POSTGRE),
        (r'supplied argument is not a valid PostgreSQL result', dbms.POSTGRE),
        (r'unterminated quoted string at or near', dbms.POSTGRE),
        (r'pg_query() [:', dbms.POSTGRE),
        (r'pg_exec() [:', dbms.POSTGRE),
        # MYSQL
        (r'supplied argument is not a valid MySQL', dbms.MYSQL),
        (r'Column count doesn\'t match value count at row', dbms.MYSQL),
        (r'mysql_fetch_array()', dbms.MYSQL),
        (r'mysql_', dbms.MYSQL),
        (r'on MySQL result index', dbms.MYSQL),
        (r'You have an error in your SQL syntax;', dbms.MYSQL),
        (r'You have an error in your SQL syntax near', dbms.MYSQL),
        (r'MySQL server version for the right syntax to use', dbms.MYSQL),
        (r'Division by zero in', dbms.MYSQL),
        (r'not a valid MySQL result', dbms.MYSQL),
        (r'[MySQL][ODBC', dbms.MYSQL),
        (r"Column count doesn't match", dbms.MYSQL),
        (r"the used select statements have different number of columns",
         dbms.MYSQL),
        (r"DBD::mysql::st execute failed", dbms.MYSQL),
        (r"DBD::mysql::db do failed:", dbms.MYSQL),
        # Informix
        (r'com.informix.jdbc', dbms.INFORMIX),
        (r'Dynamic Page Generation Error:', dbms.INFORMIX),
        (r'An illegal character has been found in the statement',
         dbms.INFORMIX),
        (r'[Informix]', dbms.INFORMIX),
        (r'<b>Warning</b>:  ibase_', dbms.INTERBASE),
        (r'Dynamic SQL Error', dbms.INTERBASE),
        # DML
        (r'[DM_QUERY_E_SYNTAX]', dbms.DMLDATABASE),
        (r'has occurred in the vicinity of:', dbms.DMLDATABASE),
        (r'A Parser Error (syntax error)', dbms.DMLDATABASE),
        # Java
        (r'java.sql.SQLException', dbms.JAVA),
        (r'Unexpected end of command in statement', dbms.JAVA),
        # Coldfusion
        (r'[Macromedia][SQLServer JDBC Driver]', dbms.MSSQL),
        # SQLite
        (r'could not prepare statement', dbms.SQLITE),
        # Generic errors..
        (r'Unknown column', dbms.UNKNOWN),
        (r'where clause', dbms.UNKNOWN),
        (r'SqlServer', dbms.UNKNOWN),
        (r'syntax error', dbms.UNKNOWN))
    _multi_in = multi_in(x[0] for x in SQL_ERRORS_STR)

    SQL_ERRORS_RE = (
        # ASP / MSSQL
        (r"Procedure '[^']+' requires parameter '[^']+'", dbms.MSSQL),
        # ORACLE
        (r'PLS-[0-9][0-9][0-9][0-9]', dbms.ORACLE),
        (r'ORA-[0-9][0-9][0-9][0-9]', dbms.ORACLE),
        # MYSQL
        (r"Table '[^']+' doesn't exist", dbms.MYSQL),
        # Generic errors..
        (r'SELECT .*? FROM .*?', dbms.UNKNOWN),
        (r'UPDATE .*? SET .*?', dbms.UNKNOWN),
        (r'INSERT INTO .*?', dbms.UNKNOWN),
    )
    _multi_re = multi_re(SQL_ERRORS_RE)

    # Note that these payloads are similar but they do generate different errors
    # depending on the SQL query context they are used. Removing one or the
    # other will lower our SQLMap testenv coverage
    SQLI_STRINGS = (u"a'b\"c'd\"", u"1'2\"3")

    SQLI_MESSAGE = (
        u'A SQL error was found in the response supplied by '
        u'the web application, the error is (only a fragment is '
        u'shown): "%s". The error was found on response with id %s.')

    def __init__(self):
        AuditPlugin.__init__(self)

    def audit(self, freq, orig_response):
        """
        Tests an URL for SQL injection vulnerabilities.

        :param freq: A FuzzableRequest
        """
        mutants = create_mutants(freq,
                                 self.SQLI_STRINGS,
                                 orig_resp=orig_response)

        self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants,
                                      self._analyze_result)

    def _analyze_result(self, mutant, response):
        """
        Analyze results of the _send_mutant method.
        """
        sql_error_list = self._findsql_error(response)
        orig_resp_body = mutant.get_original_response_body()

        for sql_error_string, dbms_type in sql_error_list:
            if sql_error_string not in orig_resp_body:
                if self._has_no_bug(mutant):
                    # Create the vuln,
                    desc = 'SQL injection in a %s was found at: %s'
                    desc = desc % (dbms_type, mutant.found_at())

                    v = Vuln.from_mutant('SQL injection', desc, severity.HIGH,
                                         response.id, self.get_name(), mutant)

                    v.add_to_highlight(sql_error_string)
                    v['error'] = sql_error_string
                    v['db'] = dbms_type

                    self.kb_append_uniq(self, 'sqli', v)
                    break

    def _findsql_error(self, response):
        """
        This method searches for SQL errors in html's.

        :param response: The HTTP response object
        :return: A list of errors found on the page
        """
        res = []

        for match in self._multi_in.query(response.body):
            om.out.information(self.SQLI_MESSAGE % (match, response.id))
            dbms_type = [x[1] for x in self.SQL_ERRORS_STR if x[0] == match][0]
            res.append((match, dbms_type))

        for match, _, regex_comp, dbms_type in self._multi_re.query(
                response.body):
            om.out.information(self.SQLI_MESSAGE %
                               (match.group(0), response.id))
            res.append((match.group(0), dbms_type))

        return res

    def get_long_desc(self):
        """
        :return: A DETAILED description of the plugin functions and features.
        """
        return """
Ejemplo n.º 9
0
along with w3af; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

"""
import re
from w3af.core.data.esmre.multi_re import multi_re

SOURCE_CODE = (
    ('<\?(?! *xml)(?!xpacket).*\?>', 'PHP'),
    ('<%.*?%>', 'ASP or JSP'),
    ('<jsp:.*?>', 'JSP'),
    ('<!--\s*%.*?%(--)?>', 'PHP'),
    ('<!--\s*\?.*?\?(--)?>', 'ASP or JSP'),
    ('<!--\s*jsp:.*?(--)?>', 'JSP'),
)

_multi_re = multi_re(SOURCE_CODE, re.IGNORECASE | re.DOTALL)


def is_source_file(file_content):
    """
    :param file_content: The content of the http response body to analyze
    :return: A tuple with:
                a re.match object if the file_content matches a source code file,
                a string with the source code programming language
    """
    for match, _, _, lang in _multi_re.query(file_content):
        return (match, lang)

    return (None, None)
Ejemplo n.º 10
0
class error_pages(GrepPlugin):
    """
    Grep every page for error pages.

    :author: Andres Riancho ([email protected])
    """

    ERROR_PAGES = (
        '<H1>Error page exception</H1>',
        # This signature fires up also in default 404 pages of aspx which
        # generates a lot of noise, so ... disabling it
        #mesg.append('<span><H1>Server Error in ',
        '<h2> <i>Runtime Error</i> </h2></span>',
        '<h2> <i>Access is denied</i> </h2></span>',
        '<H3>Original Exception: </H3>',
        'Server object error',
        'invalid literal for int()',
        'exceptions.ValueError',
        '<font face="Arial" size=2>Type mismatch: ',
        '[an error occurred while processing this directive]',
        '<HTML><HEAD><TITLE>Error Occurred While Processing Request</TITLE>'
        '</HEAD><BODY><HR><H3>Error Occurred While Processing Request</H3><P>',

        # VBScript
        '<p>Microsoft VBScript runtime </font>',
        "<font face=\"Arial\" size=2>error '800a000d'</font>",

        # nwwcgi errors
        '<TITLE>nwwcgi Error',

        # ASP error I found during a pentest, the ASP used a foxpro db, not a
        # SQL injection
        '<font face="Arial" size=2>error \'800a0005\'</font>',
        '<h2> <i>Runtime Error</i> </h2></span>',
        # Some error in ASP when using COM objects.
        'Operation is not allowed when the object is closed.',
        # An error when ASP tries to include something and it fails
        '<p>Active Server Pages</font> <font face="Arial" size=2>error \'ASP 0126\'</font>',

        # ASPX
        '<b> Description: </b>An unhandled exception occurred during the execution of the'
        ' current web request',

        # Struts
        '] does not contain handler parameter named',

        # PHP
        '<b>Warning</b>: ',
        'No row with the given identifier',
        'open_basedir restriction in effect',
        "eval()'d code</b> on line <b>",
        "Cannot execute a blank command in",
        "Fatal error</b>:  preg_replace",
        "thrown in <b>",
        "#0 {main}",
        "Stack trace:",
        "</b> on line <b>",

        # python
        "PythonHandler django.core.handlers.modpython",
        "t = loader.get_template(template_name) # You need to create a 404.html template.",
        '<h2>Traceback <span>(innermost last)</span></h2>',

        # Java
        '[java.lang.',
        'class java.lang.',
        'java.lang.NullPointerException',
        'java.rmi.ServerException',
        'at java.lang.',
        'onclick="toggle(\'full exception chain stacktrace\')"',
        'at org.apache.catalina',
        'at org.apache.coyote.',
        'at org.apache.tomcat.',
        'at org.apache.jasper.',

        # ruby
        '<h1 class="error_title">Ruby on Rails application could not be started</h1>',

        # Coldfusion
        '<title>Error Occurred While Processing Request</title></head><body><p></p>',
        '<HTML><HEAD><TITLE>Error Occurred While Processing Request</TITLE></HEAD><BODY><HR><H3>',
        '<TR><TD><H4>Error Diagnostic Information</H4><P><P>',
        '<li>Search the <a href="http://www.macromedia.com/support/coldfusion/" '
        'target="new">Knowledge Base</a> to find a solution to your problem.</li>',

        # http://www.programacion.net/asp/articulo/kbr_execute/
        'Server.Execute Error',

        # IIS
        '<h2 style="font:8pt/11pt verdana; color:000000">HTTP 403.6 - Forbidden: IP address rejected<br>',
        '<TITLE>500 Internal Server Error</TITLE>',
    )
    _multi_in = multi_in(ERROR_PAGES)

    VERSION_REGEX = (
        ('<address>(.*?)</address>', 'Apache'),
        ('<HR size="1" noshade="noshade"><h3>(.*?)</h3></body>',
         'Apache Tomcat'),
        ('<a href="http://www.microsoft.com/ContentRedirect.asp\?prd=iis&sbp=&pver=(.*?)&pid=&ID',
         'IIS'),

        # <b>Version Information:</b>&nbsp;Microsoft .NET Framework Version:1.1.4322.2300; ASP.NET Version:1.1.4322.2300
        ('<b>Version Information:</b>&nbsp;(.*?)\n', 'ASP .NET'))
    _multi_re = multi_re(VERSION_REGEX)

    def __init__(self):
        GrepPlugin.__init__(self)

        self._already_reported_versions = []
        self._compiled_regex = []

    def grep(self, request, response):
        """
        Plugin entry point, find the error pages and report them.

        :param request: The HTTP request object.
        :param response: The HTTP response object
        :return: None
        """
        if not response.is_text_or_html():
            return

        self.find_error_page(request, response)
        self.find_version_numbers(request, response)

    def find_error_page(self, request, response):
        for msg in self._multi_in.query(response.body):

            desc = 'The URL: "%s" contains the descriptive error: "%s".'
            desc = desc % (response.get_url(), msg)
            i = Info('Descriptive error page', desc, response.id,
                     self.get_name())
            i.set_url(response.get_url())
            i.add_to_highlight(msg)

            self.kb_append_uniq(self, 'error_page', i, 'URL')

            # There is no need to report more than one info for the same result,
            # the user will read the info object and analyze it even if we
            # report it only once. If we report it twice, he'll get mad ;)
            break

    def find_version_numbers(self, request, response):
        """
        Now i'll check if I can get a version number from the error page
        This is common in apache, tomcat, etc...
        """
        if 400 < response.get_code() < 600:

            for match, _, _, server in self._multi_re.query(response.body):
                match_string = match.group(0)
                if match_string not in self._already_reported_versions:
                    # Save the info obj
                    desc = 'An error page sent this %s version: "%s".'
                    desc %= (server, match_string)

                    i = Info('Error page with information disclosure', desc,
                             response.id, self.get_name())
                    i.set_url(response.get_url())
                    i.add_to_highlight(server)
                    i.add_to_highlight(match_string)

                    kb.kb.append(self, 'server', i)
                    kb.kb.raw_write(self, 'server', match_string)

                    self._already_reported_versions.append(match_string)

    def get_long_desc(self):
        """
        :return: A DETAILED description of the plugin functions and features.
        """
        return """
Ejemplo n.º 11
0
class http_in_body(GrepPlugin):
    """
    Search for HTTP request/response string in response body.
    :author: Andres Riancho ([email protected])
    """

    HTTP = (
        # GET / HTTP/1.0
        ('[a-zA-Z]{3,6} .*? HTTP/1.[01]', 'REQUEST'),
        # HTTP/1.1 200 OK
        ('HTTP/1.[01] [0-9][0-9][0-9] [a-zA-Z]*', 'RESPONSE'))
    _multi_re = multi_re(HTTP)

    def __init__(self):
        GrepPlugin.__init__(self)

    def grep(self, request, response):
        """
        Plugin entry point.

        :param request: The HTTP request object.
        :param response: The HTTP response object
        :return: None, all results are saved in the kb.
        """
        # 501 Code is "Not Implemented" which in some cases responds with
        # this in the body:
        # <body><h2>HTTP/1.1 501 Not Implemented</h2></body>
        # Which creates a false positive.
        if response.get_code() != 501 \
                and response.is_text_or_html():

            body_without_tags = response.get_clear_text_body()
            if body_without_tags is None:
                return

            uri = response.get_uri()

            for match, _, _, reqres in self._multi_re.query(body_without_tags):

                if reqres == 'REQUEST':
                    desc = 'An HTTP request was found in the HTTP body of' \
                           ' a response.'
                    i = Info('HTTP Request in HTTP body', desc, response.id,
                             self.get_name())
                    i.set_uri(uri)
                    i.add_to_highlight(match.group(0))
                    kb.kb.append(self, 'request', i)

                if reqres == 'RESPONSE':
                    desc = 'An HTTP response was found in the HTTP body of' \
                           ' a response.'
                    i = Info('HTTP Response in HTTP body', desc, response.id,
                             self.get_name())
                    i.set_uri(uri)
                    i.add_to_highlight(match.group(0))
                    kb.kb.append(self, 'response', i)

    def end(self):
        """
        This method is called when the plugin wont be used anymore.
        """
        item_fmt = '- %s  (id: %s)'
        msg = 'The following URLs have an HTTP %s in the HTTP' \
              ' response body:'

        for info_type in ['request', 'response']:
            if kb.kb.get('http_in_body', info_type):

                om.out.information(msg % info_type)

                for i in kb.kb.get('http_in_body', info_type):
                    om.out.information(item_fmt % (i.get_uri(), i.get_id()))

    def get_long_desc(self):
        """
        :return: A DETAILED description of the plugin functions and features.
        """
        return """\
Ejemplo n.º 12
0
Archivo: lfi.py Proyecto: zcr214/w3af
class lfi(AuditPlugin):
    """
    Find local file inclusion vulnerabilities.
    :author: Andres Riancho ([email protected])
    """

    file_pattern_multi_in = multi_in(FILE_PATTERNS)
    file_read_error_multi_re = multi_re(FILE_OPEN_ERRORS)

    def audit(self, freq, orig_response):
        """
        Tests an URL for local file inclusion vulnerabilities.

        :param freq: A FuzzableRequest
        """
        mutants = create_mutants(freq,
                                 self.get_lfi_tests(freq),
                                 orig_resp=orig_response)

        self._send_mutants_in_threads(self._uri_opener.send_mutant,
                                      mutants,
                                      self._analyze_result,
                                      grep=False)

    def get_lfi_tests(self, freq):
        """
        :param freq: The fuzzable request we're analyzing
        :return: The paths to test
        """
        #
        #   Add some tests which try to read "self"
        #   http://host.tld/show_user.php?id=show_user.php
        #
        lfi_tests = [
            freq.get_url().get_file_name(),
            '/%s' % freq.get_url().get_file_name()
        ]

        #
        #   Add some tests which try to read common/known files
        #
        lfi_tests.extend(self._get_common_file_list(freq.get_url()))

        return lfi_tests

    def _get_common_file_list(self, orig_url):
        """
        This method returns a list of local files to try to include.

        :return: A string list, see above.
        """
        local_files = []

        extension = orig_url.get_extension()

        # I will only try to open these files, they are easy to identify of they
        # echoed by a vulnerable web app and they are on all unix or windows
        # default installs. Feel free to mail me (Andres Riancho) if you know
        # about other default files that could be installed on AIX ? Solaris ?
        # and are not /etc/passwd
        if cf.cf.get('target_os') in {'unix', 'unknown'}:
            local_files.append('/../' * 15 + 'etc/passwd')
            local_files.append('../' * 15 + 'etc/passwd')

            local_files.append('/../' * 15 + 'etc/passwd\0')
            local_files.append('/../' * 15 + 'etc/passwd\0.html')
            local_files.append('/etc/passwd')

            # This test adds support for finding vulnerabilities like this one
            # http://website/zen-cart/extras/curltest.php?url=file:///etc/passwd
            local_files.append('file:///etc/passwd')

            local_files.append('/etc/passwd\0')
            local_files.append('/etc/passwd\0.html')

            if extension != '':
                local_files.append('/etc/passwd%00.' + extension)
                local_files.append('/../' * 15 + 'etc/passwd%00.' + extension)

        if cf.cf.get('target_os') in {'windows', 'unknown'}:
            local_files.append('/../' * 15 + 'boot.ini')
            local_files.append('../' * 15 + 'boot.ini')

            local_files.append('/../' * 15 + 'boot.ini\0')
            local_files.append('/../' * 15 + 'boot.ini\0.html')

            local_files.append('C:\\boot.ini')
            local_files.append('C:\\boot.ini\0')
            local_files.append('C:\\boot.ini\0.html')

            local_files.append('%SYSTEMROOT%\\win.ini')
            local_files.append('%SYSTEMROOT%\\win.ini\0')
            local_files.append('%SYSTEMROOT%\\win.ini\0.html')

            # file:// URIs for windows , docs here: http://goo.gl/A9Mvux
            local_files.append('file:///C:/boot.ini')
            local_files.append('file:///C:/win.ini')

            if extension != '':
                local_files.append('C:\\boot.ini%00.' + extension)
                local_files.append('%SYSTEMROOT%\\win.ini%00.' + extension)

        return local_files

    def _analyze_result(self, mutant, response):
        """
        Analyze results of the _send_mutant method.
        Try to find the local file inclusions.
        """
        #
        #   I will only report the vulnerability once.
        #
        if self._has_bug(mutant):
            return

        #
        #   Identify the vulnerability
        #
        for file_pattern_match in self._find_common_file_fragments(response):
            if file_pattern_match not in mutant.get_original_response_body():

                desc = 'Local File Inclusion was found at: %s'
                desc %= mutant.found_at()

                v = Vuln.from_mutant('Local file inclusion vulnerability',
                                     desc, severity.MEDIUM, response.id,
                                     self.get_name(), mutant)

                v['file_pattern'] = file_pattern_match

                v.add_to_highlight(file_pattern_match)
                self.kb_append_uniq(self, 'lfi', v)
                return

        #
        # If the vulnerability could not be identified by matching strings that
        # commonly appear in "/etc/passwd", then I'll check one more thing...
        # (note that this is run if no vulns were identified)
        #
        # http://host.tld/show_user.php?id=show_user.php
        if mutant.get_url().get_file_name() in mutant.get_token_value():
            match, lang = contains_source_code(response)
            if match:
                # We were able to read the source code of the file that is
                # vulnerable to local file read
                desc = ('An arbitrary local file read vulnerability was'
                        ' found at: %s')
                desc %= mutant.found_at()

                v = Vuln.from_mutant('Local file inclusion vulnerability',
                                     desc, severity.MEDIUM, response.id,
                                     self.get_name(), mutant)

                #
                #    Set which part of the source code to match
                #
                match_source_code = match.group(0)
                v['file_pattern'] = match_source_code

                self.kb_append_uniq(self, 'lfi', v)
                return

        #
        #   Check for interesting errors (note that this is run if no vulns were
        #   identified)
        #
        body = response.get_body()
        for _, error_str, _ in self.file_read_error_multi_re.query(body):
            if error_str not in mutant.get_original_response_body():
                desc = 'A file read error was found at: %s'
                desc %= mutant.found_at()

                i = Info.from_mutant('File read error', desc, response.id,
                                     self.get_name(), mutant)
                i.add_to_highlight(error_str)

                self.kb_append_uniq(self, 'error', i)

    def _find_common_file_fragments(self, response):
        """
        This method finds out if the local file has been successfully included
        in the resulting HTML.

        :param response: The HTTP response object
        :return: A list of errors found on the page
        """
        res = set()
        matches = self.file_pattern_multi_in.query(response.get_body())

        for file_pattern_match in matches:
            res.add(file_pattern_match)

        if len(res) == 1:
            msg = (
                'A file fragment was found. The section where the file is'
                ' included is (only a fragment is shown): "%s". This is'
                ' just an informational message, which might be related'
                '  to a vulnerability and was found on response with id %s.')
            om.out.debug(msg % (list(res)[0], response.id))

        if len(res) > 1:
            msg = ('File fragments have been found. The following is a list'
                   ' of file fragments that were returned by the web'
                   ' application while testing for local file inclusion: \n')

            for file_pattern_match in res:
                msg += '- "%s" \n' % file_pattern_match

            msg += ('This is just an informational message, which might be'
                    ' related to a vulnerability and was found in response'
                    ' with id %s.' % response.id)

            om.out.debug(msg)

        return res

    def get_long_desc(self):
        """
        :return: A DETAILED description of the plugin functions and features.
        """
        return """
along with w3af; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

"""
import re
from w3af.core.data.esmre.multi_re import multi_re

SOURCE_CODE = (
    ("<\?(?! *xml)(?!xpacket).*\?>", "PHP"),
    ("<%.*?%>", "ASP or JSP"),
    ("<jsp:.*?>", "JSP"),
    ("<!--\s*%.*?%(--)?>", "PHP"),
    ("<!--\s*\?.*?\?(--)?>", "ASP or JSP"),
    ("<!--\s*jsp:.*?(--)?>", "JSP"),
)

_multi_re = multi_re(SOURCE_CODE, re.IGNORECASE | re.DOTALL)


def is_source_file(file_content):
    """
    :param file_content: The content of the http response body to analyze
    :return: A tuple with:
                a re.match object if the file_content matches a source code file,
                a string with the source code programming language
    """
    for match, _, _, lang in _multi_re.query(file_content):
        return (match, lang)

    return (None, None)