Skip to content

Commit

Permalink
Improved errors shown in KB
Browse files Browse the repository at this point in the history
  • Loading branch information
andresriancho committed Nov 28, 2019
1 parent ce576bf commit 8bef36c
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 34 deletions.
98 changes: 72 additions & 26 deletions w3af/core/controllers/plugins/auth_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
from collections import deque

import w3af.core.controllers.output_manager as om
import w3af.core.data.kb.knowledge_base as kb
import w3af.core.data.kb.config as cf
Expand All @@ -39,8 +41,7 @@ class AuthPlugin(Plugin):
:author: Dmitriy V. Simonov ( dsimonov@yandex-team.com )
"""

MAX_FAILED_LOGIN_COUNT = 3
MAX_CONSECUTIVE_FAILED_LOGIN_COUNT = 3
BLACKLIST_LOGIN_URL_MESSAGE = ('The following URLs were added to the audit blacklist:\n'
'\n'
' - %s\n'
Expand All @@ -66,7 +67,8 @@ def __init__(self):
self._http_response_ids = []
self._log_messages = []
self._attempt_login = True
self._failed_login_count = 0

self._login_result_log = deque(maxlen=500)

def login(self):
"""
Expand Down Expand Up @@ -177,36 +179,75 @@ def _get_main_authentication_url(self):
"""
raise NotImplementedError

def _max_consecutive_failed_login_count_exceeded(self):
if len(self._login_result_log) < self.MAX_CONSECUTIVE_FAILED_LOGIN_COUNT:
return False

# This awful range statement generates -1, -2, -3 when
# self.MAX_CONSECUTIVE_FAILED_LOGIN_COUNT is set to 3.
for i in range(-1, - self.MAX_CONSECUTIVE_FAILED_LOGIN_COUNT - 1, -1):

# If there is at least one successful login in the last three
# then the max consecutive failed login was not exceeded
if self._login_result_log[i]:
return False

return True

def _all_login_attempts_failed(self):
for login_result in self._login_result_log:
if login_result:
return False

return True

def _handle_authentication_failure(self):
self._failed_login_count += 1
self._login_result_log.append(False)

if self._failed_login_count == self.MAX_FAILED_LOGIN_COUNT:
if self._max_consecutive_failed_login_count_exceeded():
msg = ('The authentication plugin failed %s consecutive times to'
' get a valid application session using the user-provided'
' configuration settings. Disabling the `%s` authentication'
' plugin.')
args = (self._failed_login_count, self.get_name())
self._log_error(msg % args)
' configuration settings.\n'
'\n'
'The `%s` authentication plugin will be disabled.')
args = (self.MAX_CONSECUTIVE_FAILED_LOGIN_COUNT, self.get_name())

self._log_info_to_kb()
title = 'Authentication failure'
message = msg % args

self._log_info_to_kb(title, message, include_log_messages=True)
self._log_error(message)
self._attempt_login = False

def end(self):
if self._failed_login_count:
msg = ('The `%s` authentication plugin failed %i times to get'
' a valid application session using the user-provided'
#
# Please note that the auth_session_plugin.py file reports
# "Unstable application session" using the data collected from the
# has_active_session method
#
# This message is different from the one on that file. This message is
# shown to let the user know that his configuration is incorrect. The
# reason for having this in end() and in _handle_authentication_failure()
# is that in some cases the plugin will run only two times, not reach
# self.MAX_FAILED_LOGIN_COUNT and never report the issue to the user
#
if self._all_login_attempts_failed():
msg = ('The `%s` authentication plugin was never able to authenticate'
' and get a valid application session using the user-provided'
' configuration settings')
args = (self.get_name(), self._failed_login_count,)
args = (self.get_name(),)

self._log_error(msg % args)
title = 'Authentication failure'
message = msg % args

self._log_info_to_kb()
self._log_info_to_kb(title, message, include_log_messages=True)
self._log_error(message)
self._attempt_login = False

def _handle_authentication_success(self):
self._failed_login_count = 0
self._login_result_log.append(True)

def _log_info_to_kb(self):
def _log_info_to_kb(self, title, message, include_log_messages=True):
"""
This method creates an Info object containing information about failed
authentication attempts and stores it in the knowledge base.
Expand All @@ -218,14 +259,19 @@ def _log_info_to_kb(self):
:return: None
"""
desc = ('The authentication plugin failed to get a valid application'
' session using the user-provided configuration settings.\n'
'\n'
'The plugin generated the following log messages:\n'
'\n')
desc += '\n'.join(self._log_messages)

i = Info('Authentication failure',
desc = message

if include_log_messages:
log_messages = ' - ' + '\n - '.join(self._log_messages)
args = (message, log_messages)
msg_fmt = ('%s\n'
'\n'
'The following are the last log messages from the authentication plugin:\n'
'\n'
'%s')
desc = msg_fmt % args

i = Info(title,
desc,
self._http_response_ids,
self.get_name())
Expand Down
87 changes: 79 additions & 8 deletions w3af/plugins/tests/auth/test_autocomplete.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@
along with w3af; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import unittest

from httpretty import httpretty
from mock import Mock

import w3af.core.data.kb.knowledge_base as kb

from w3af.plugins.tests.helper import PluginTest, PluginConfig, MockResponse
from w3af.plugins.auth.autocomplete import autocomplete
from w3af.core.data.parsers.doc.url import URL

USER = 'user@mail.com'
Expand Down Expand Up @@ -203,18 +207,85 @@ def test_handle_invalid_credentials(self):
info = infos[0]

expected_desc = (
'The authentication plugin failed to get a valid application session using the user-provided configuration settings.\n'
'The `autocomplete` authentication plugin was never able to authenticate and get a valid application session using the user-provided configuration settings\n'
'\n'
'The plugin generated the following log messages:\n'
'The following are the last log messages from the authentication plugin:\n'
'\n'
'Logging into the application with user: user@mail.com\n'
'Login form with action http://w3af.org/login_post.py found in HTTP response with ID 21\n'
'Login form sent to http://w3af.org/login_post.py in HTTP request ID 22\n'
'Checking if session for user user@mail.com is active\n'
'User "user@mail.com" is NOT logged into the application, the `check_string` was not found in the HTTP response with ID 23.\n'
'The `autocomplete` authentication plugin failed 1 times to get a valid application session using the user-provided configuration settings\nThe `autocomplete` authentication plugin failed 1 times to get a valid application session using the user-provided configuration settings'
' - Logging into the application with user: user@mail.com\n'
' - Login form with action http://w3af.org/login_post.py found in HTTP response with ID 21\n'
' - Login form sent to http://w3af.org/login_post.py in HTTP request ID 22\n'
' - Checking if session for user user@mail.com is active\n'
' - User "user@mail.com" is NOT logged into the application, the `check_string` was not found in the HTTP response with ID 23.'
)

self.assertEqual(info.get_name(), 'Authentication failure')
self.assertEqual(info.get_desc(with_id=False), expected_desc)
self.assertEqual(info.get_id(), [21, 22, 23])


class TestAutocompleteAuthenticationFailure(unittest.TestCase):
def test_consecutive_authentication_failure(self):
plugin = autocomplete()
kb.kb.cleanup()

for i in xrange(autocomplete.MAX_CONSECUTIVE_FAILED_LOGIN_COUNT - 1):
plugin._log_debug(str(i))
plugin._handle_authentication_failure()

infos = kb.kb.get('authentication', 'error')
self.assertEqual(len(infos), 0)

plugin._handle_authentication_failure()

infos = kb.kb.get('authentication', 'error')
self.assertEqual(len(infos), 1)
info = infos[0]

expected_desc = ('The authentication plugin failed 3 consecutive times to get a valid application session using the user-provided configuration settings.\n'
'\n'
'The `autocomplete` authentication plugin will be disabled.\n'
'\n'
'The following are the last log messages from the authentication plugin:\n'
'\n'
' - 0\n'
' - 1')

self.assertEqual(info.get_name(), 'Authentication failure')
self.assertEqual(info.get_desc(with_id=False), expected_desc)
self.assertEqual(info.get_id(), [])

def test_mixed_authentication_results(self):
plugin = autocomplete()
kb.kb.cleanup()

for i in xrange(autocomplete.MAX_CONSECUTIVE_FAILED_LOGIN_COUNT):
plugin._log_debug(str(i))
plugin._handle_authentication_failure()
plugin._handle_authentication_success(Mock())

infos = kb.kb.get('authentication', 'error')
self.assertEqual(len(infos), 0)

plugin._handle_authentication_failure()

infos = kb.kb.get('authentication', 'error')
self.assertEqual(len(infos), 0)

def test_mixed_authentication_results_fail_fail_success(self):
plugin = autocomplete()
kb.kb.cleanup()

for i in xrange(autocomplete.MAX_CONSECUTIVE_FAILED_LOGIN_COUNT):
plugin._log_debug(str(i))

plugin._handle_authentication_failure()
plugin._handle_authentication_failure()
plugin._handle_authentication_success(Mock())

infos = kb.kb.get('authentication', 'error')
self.assertEqual(len(infos), 0)

plugin._handle_authentication_failure()

infos = kb.kb.get('authentication', 'error')
self.assertEqual(len(infos), 0)

0 comments on commit 8bef36c

Please sign in to comment.