Skip to content
Snippets Groups Projects
Commit 1c139412 authored by Zitnik, Anze's avatar Zitnik, Anze
Browse files

Adding support for w3af authenticated scans.

Squashed commit of the following:

commit 7d99f5d2c82d84e144b8f099563367a498207251
Author: Anže Žitnik <anze.zitnik@xlab.si>
Date:   Tue Apr 28 14:44:05 2020 +0200

    Updated extended_generic w3af profile to work with newer w3af. Added some config examples.

commit 644923a660f031dcfcf3947f4b744c70a1c2d467
Author: Anže Žitnik <anze.zitnik@xlab.si>
Date:   Tue Apr 28 11:35:20 2020 +0200

    Added auth_scan profile and changed configure.py to support its config. Imported extended_generic auth plugin from old version.
parent 4ab5fc63
No related branches found
No related tags found
No related merge requests found
......@@ -3,7 +3,7 @@ FROM ubuntu:18.04
COPY install/base.sh /tmp/install/
RUN chmod +x /tmp/install/base.sh && /tmp/install/base.sh
COPY install/requirements.txt install/w3af_output_fix.patch install/w3af-lz4.patch install/w3af-scans.py.patch /tmp/
COPY install/requirements.txt install/w3af_output_fix.patch install/w3af-lz4.patch install/w3af-scans.py.patch install/profiles/extended_generic.py install/profiles/auth_scan.template /tmp/
COPY install/w3af.sh /tmp/install/
RUN chmod +x /tmp/install/w3af.sh && /tmp/install/w3af.sh
......
VERSION=v1.4.1
VERSION=v1.4.2
SERVICE=vat-genscan
{
"target": {
"url": "http://172.17.0.5/"
},
"config": {
"w3af": {
"profile": "auth_scan",
"parameters": {
"username": "admin",
"password": "password",
"username_field": "username",
"password_field": "password",
"auth_url": "http://172.17.0.5/login.php",
"check_url": "http://172.17.0.5/index.php",
"check_string": "admin"
}
}
}
}
{
"target": {
"url": "http://192.168.1.184/bWAPP/"
},
"config": {
"w3af": {
"profile": "auth_scan",
"parameters": {
"username": "bee",
"password": "bug",
"username_field": "login",
"password_field": "password",
"auth_url": "http://192.168.1.184/bWAPP/login.php",
"check_url": "http://192.168.1.184/bWAPP/portal.php",
"check_string": "Welcome Bee"
}
},
"zap": {
"profile": "basic"
}
}
}
File moved
......@@ -42,6 +42,14 @@ def configure():
cscan_config["W3AF"] = {"CS_W3AF": "/service/w3af/w3af_api"}
if profile == "fast_scan":
cscan_config["W3AF"]["CS_W3AF_PROFILE"] = "/service/w3af/profiles/fast_scan.pw3af"
elif profile == "auth_scan":
cscan_config["W3AF"]["CS_W3AF_PROFILE"] = "/service/w3af/profiles/auth_scan.pw3af"
w3af_config = configparser.ConfigParser()
w3af_config.read("/service/w3af/profiles/auth_scan.template")
for key in config["config"][scanner]["parameters"]:
w3af_config['auth.extended_generic'][key] = config["config"][scanner]["parameters"][key]
with open ("/service/w3af/profiles/auth_scan.pw3af", "w") as f_out:
w3af_config.write(f_out)
else:
raise UnsupportedProfileException()
cs_scripts.append("w3af.sh")
......
[profile]
description = Profile generated using the console UI.
name = fast_scan_auth
[grep.symfony]
override = False
[grep.file_upload]
[grep.wsdl_greper]
[grep.form_autocomplete]
[grep.strange_parameters]
[grep.svn_users]
[grep.private_ip]
[grep.motw]
[grep.code_disclosure]
[grep.blank_body]
[grep.path_disclosure]
[grep.strange_http_codes]
[grep.http_auth_detect]
[grep.credit_cards]
[grep.dom_xss]
[grep.html_comments]
[grep.http_in_body]
[grep.dot_net_event_validation]
[grep.ssn]
[grep.error_500]
[grep.meta_tags]
[grep.lang]
[grep.click_jacking]
[grep.directory_indexing]
[grep.password_profiling]
[grep.get_emails]
only_target_domain = True
[grep.hash_analysis]
[grep.error_pages]
[grep.strange_reason]
[grep.user_defined_regex]
single_regex =
regex_file_path = %ROOT_PATH%/plugins/grep/user_defined_regex/empty.txt
[grep.strange_headers]
[grep.objects]
[grep.oracle]
[grep.feeds]
[grep.analyze_cookies]
[auth.extended_generic]
username = <username_value>
password = <password_value>
username_field = <username_field>
password_field = <password_field>
auth_url = <auth_url>
check_url = <check_url>
check_string = <check_string>
[audit.xss]
persistent_xss = True
[audit.sqli]
[crawl.web_spider]
only_forward = False
follow_regex = .*
ignore_regex =
[output.console]
verbose = False
[misc-settings]
fuzz_cookies = False
fuzz_form_files = True
fuzz_url_filenames = False
fuzz_url_parts = False
fuzzed_files_extension = gif
fuzzable_headers =
form_fuzzing_mode = tmb
stop_on_first_exception = False
max_discovery_time = 120
interface = ppp0
local_ip_address = 10.32.31.5
non_targets =
msf_location = /opt/metasploit3/bin/
[http-settings]
timeout = 0
headers_file =
basic_auth_user =
basic_auth_passwd =
basic_auth_domain =
ntlm_auth_domain =
ntlm_auth_user =
ntlm_auth_passwd =
ntlm_auth_url =
cookie_jar_file =
ignore_session_cookies = False
proxy_port = 8080
proxy_address =
user_agent = w3af.org
rand_user_agent = False
max_file_size = 400000
max_http_retries = 2
max_requests_per_second = 0
always_404 =
never_404 =
string_match_404 =
url_parameter =
from urllib import urlencode
import w3af.core.controllers.output_manager as om
from w3af.core.data.options.opt_factory import opt_factory
from w3af.core.data.options.option_list import OptionList
from w3af.core.controllers.plugins.auth_plugin import AuthPlugin
from w3af.core.controllers.exceptions import BaseFrameworkException
from lxml import etree
class extended_generic(AuthPlugin):
"""
Generic authentication plugin extended to support CSRF tokens in login forms.
"""
def __init__(self):
AuthPlugin.__init__(self)
# User configuration
self.username = ''
self.password = ''
self.username_field = ''
self.password_field = ''
self.auth_url = 'http://host.tld/'
self.check_url = 'http://host.tld/'
self.check_string = ''
# Internal attributes
self._attempt_login = True
def login(self):
"""
Login to the application.
"""
#
# In some cases the authentication plugin is incorrectly configured and
# we don't want to keep trying over and over to login when we know it
# will fail
#
if not self._attempt_login:
return False
#
# Create a new debugging ID for each login() run
#
self._new_debugging_id()
self._clear_log()
msg = 'Logging into the application using %s' % self.username
om.out.debug(msg)
#GET the login page
http_response = self._uri_opener.GET(self.auth_url, grep=False,
cache=False)
body = http_response.get_body()
ht = etree.HTML(body)
forms = ht.xpath('//form')
accepted_form = False
data_dict = dict()
for f in forms:
if f.get('method').lower()!='post' or not bool(f.xpath("//input[@name='%s']" % self.username_field)) or not bool(f.xpath("//input[@name='%s']" % self.password_field)):
continue
inputs = f.xpath("//input")
for i in inputs:
print i.values(), i.keys()
if i.get('name') in [self.username_field, self.password_field]:
#if i.get('type')=='submit' or i.get('name') in [self.username_field, self.password_field]:
continue
print i.values(), i.keys()
data_dict[i.get('name')]=i.get('value')
accepted_form = True
if not accepted_form:
raise Exception("No matching form found at login page")
data_dict[self.username_field]=self.username
data_dict[self.password_field]=self.password
data = urlencode(data_dict)
try:
http_response = self._uri_opener.POST(self.auth_url,
data=data,
grep=False,
cache=False,
follow_redirects=True,
debugging_id=self._debugging_id)
except Exception, e:
msg = 'Failed to login to the application because of exception: %s'
self._log_debug(msg % e)
return False
self._log_http_response(http_response)
#
# Check if we're logged in
#
if not self.has_active_session():
self._log_info_to_kb()
return False
om.out.debug('Login success for %s' % self.username)
return True
def logout(self):
"""
User logout
"""
return None
def has_active_session(self):
"""
Check user session
"""
# Create a new debugging ID for each has_active_session() run
self._new_debugging_id()
msg = 'Checking if session for user %s is active'
self._log_debug(msg % self.username)
try:
http_response = self._uri_opener.GET(self.check_url,
grep=False,
cache=False,
follow_redirects=True,
debugging_id=self._debugging_id)
except Exception, e:
msg = 'Failed to check if session is active because of exception: %s'
self._log_debug(msg % e)
return False
self._log_http_response(http_response)
body = http_response.get_body()
logged_in = self.check_string in body
msg_yes = 'User "%s" is currently logged into the application'
msg_yes %= (self.username,)
msg_no = ('User "%s" is NOT logged into the application, the'
' `check_string` was not found in the HTTP response'
' with ID %s.')
msg_no %= (self.username, http_response.id)
msg = msg_yes if logged_in else msg_no
self._log_debug(msg)
return logged_in
def get_options(self):
"""
:return: A list of option objects for this plugin.
"""
options = [
('username', self.username, 'string',
'Username for using in the authentication process'),
('password', self.password, 'string',
'Password for using in the authentication process'),
('username_field', self.username_field,
'string', 'Username parameter name (ie. "uname" if the HTML looks'
' like <input type="text" name="uname">...)'),
('password_field', self.password_field,
'string', 'Password parameter name (ie. "pwd" if the HTML looks'
' like <input type="password" name="pwd">...)'),
('auth_url', self.auth_url, 'url',
'URL where the username and password will be sent using a POST'
' request'),
('check_url', self.check_url, 'url',
'URL used to verify if the session is still active by looking for'
' the check_string.'),
('check_string', self.check_string, 'string',
'String for searching on check_url page to determine if the'
'current session is active.'),
]
ol = OptionList()
for o in options:
ol.add(opt_factory(o[0], o[1], o[3], o[2], help=o[3]))
return ol
def set_options(self, options_list):
"""
This method sets all the options that are configured using
the user interface generated by the framework using
the result of get_options().
:param options_list: A dict with the options for the plugin.
:return: No value is returned.
"""
self.username = options_list['username'].get_value()
self.password = options_list['password'].get_value()
self.username_field = options_list['username_field'].get_value()
self.password_field = options_list['password_field'].get_value()
self.check_string = options_list['check_string'].get_value()
self.auth_url = options_list['auth_url'].get_value()
self.check_url = options_list['check_url'].get_value()
missing_options = []
for o in options_list:
if not o.get_value():
missing_options.append(o.get_name())
if missing_options:
msg = ("All parameters are required and can't be empty. The"
" missing parameters are %s")
raise BaseFrameworkException(msg % ', '.join(missing_options))
def get_long_desc(self):
"""
:return: A DETAILED description of the plugin functions and features.
"""
return """
This authentication plugin can login to Web applications which use
common authentication schemes.
Also tries to use additional parameters found in login forms for CSRF prevention.
Seven configurable parameters exist:
- username
- password
- username_field
- password_field
- auth_url
- check_url
- check_string
"""
......@@ -35,3 +35,7 @@ patch /service/w3af/w3af/core/ui/api/utils/scans.py /tmp/w3af_output_fix.patch
patch /service/w3af/w3af/core/controllers/dependency_check/requirements.py /tmp/w3af-lz4.patch
patch /service/w3af/w3af/core/ui/api/utils/scans.py /tmp/w3af-scans.py.patch
#additional profiles and plugins
cp /tmp/extended_generic.py /service/w3af/w3af/plugins/auth/
cp /tmp/auth_scan.template /service/w3af/profiles/
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment