What changes to authentication script is needed so it can follow redirect?

757 views
Skip to first unread message

Kaspars

unread,
May 18, 2017, 1:46:11 AM5/18/17
to OWASP ZAP User Group

I am trying to make automated scan script to test DVWA (Damn Vulnerable Web Application

).

But I faced issue when spider is not recognizing response correctly (index.php is not found and active scan is not attacking it).

I think root cause is in authentication script but do not know how to correct it.

When I do page browsing with Firefox then after Post request to login.php there is 302 redirect to index.php.


But when I authenticate by script then after Post to login.php I receive response from index.php without mention that it is separate page. So I think this is the reason why spider is not detecting index.php page.


I am using standard auth script:


function authenticate(helper, paramsValues, credentials) {

    var loginUrl = paramsValues.get("LoginURL");

    var csrfTokenName = paramsValues.get("CSRFField");

    var csrfTokenValue = extractInputFieldValue(getPageContent(helper, loginUrl), csrfTokenName);

    var postData = paramsValues.get("POSTData");


    postData = postData.replace('{%username%}', encodeURIComponent(credentials.getParam("Username")));

    postData = postData.replace('{%password%}', encodeURIComponent(credentials.getParam("Password")));

    postData = postData.replace('{%' + csrfTokenName + '%}', encodeURIComponent(csrfTokenValue));


    var msg = sendAndReceive(helper, loginUrl, postData);

    return msg;

}


function getRequiredParamsNames() {

    return [ "LoginURL", "CSRFField", "POSTData" ];

}


function getOptionalParamsNames() {

    return [];

}


function getCredentialsParamsNames() {

    return [ "Username", "Password" ];

}


function getPageContent(helper, url) {

    var msg = sendAndReceive(helper, url);

    return msg.getResponseBody().toString();

}


function sendAndReceive(helper, url, redirectUrl, postData) {

    var msg = helper.prepareMessage();

    var method = "GET";

    if (postData) {

        method = "POST";

        msg.setRequestBody(postData);

    }


    var requestUri = new org.apache.commons.httpclient.URI(url, true); //true to false

    var requestHeader = new org.parosproxy.paros.network.HttpRequestHeader(method, requestUri, "HTTP/1.0");

    msg.setRequestHeader(requestHeader);

    helper.sendAndReceive(msg);


    return msg;

}


function extractInputFieldValue(page, fieldName) {

    // Rhino:

    //var src = new net.htmlparser.jericho.Source(page);

    // Nashorn:

     var Source = Java.type("net.htmlparser.jericho.Source");

     var src = new Source(page);


    var it = src.getAllElements('input').iterator();


    while (it.hasNext()) {

        var element = it.next();

        if (element.getAttributeValue('name') == fieldName) {

            return element.getAttributeValue('value');

        }

    }

    return '';

}


I tried to modify function sendAndReceive so it will send second request to index.php page:


var requestUri = new org.apache.commons.httpclient.URI(url, false); //true to false

    var requestHeader = new org.parosproxy.paros.network.HttpRequestHeader(method, requestUri, "HTTP/1.0");

    msg.setRequestHeader(requestHeader);

    helper.sendAndReceive(msg, false); // don't follow redirects in order to set correctly the cookie

    AuthenticationHelper.addAuthMessageToHistory(msg);

    requestUri = new org.apache.commons.httpclient.URI(redirectUrl, false);

    requestMethod = HttpRequestHeader.GET;

    requestHeader = new org.parosproxy.paros.network.HttpRequestHeader(requestMethod, requestUri, "HTTP/1.0");

    msg = helper.prepareMessage();

    msg.setRequestHeader(requestHeader);

    msg.getRequestHeader().setContentLength(msg.getRequestBody().length());

    helper.sendAndReceive(msg, true);

return msg;


but in this case ZAP is sending only get request to login.php.



What is the correct way to avoid this issue or what changes are needed to auth script?


kingthorin+owaspzap

unread,
May 18, 2017, 7:28:46 AM5/18/17
to OWASP ZAP User Group

Kaspars

unread,
May 19, 2017, 4:05:25 AM5/19/17
to OWASP ZAP User Group

Yes, checked. But didn't understand how to apply this to script. 

I will appreciate if somebody could point to solution.

 

I am trying to run scan from script:


#!/usr/bin/env python

import time
from pprint import pprint
from zapv2 import ZAPv2
import urllib

apiKey = '8peg6hqbq4ci6f4jdkabb55pp4' 
#applicationURL = ['http://127.0.0.1:85/index.php']
applicationURL = ['']
zap = ZAPv2(apikey=apiKey, proxies={'http': 'http://127.0.0.1:8081', 'https': 'http://127.0.0.1:8081'})
core = zap.core
sessionName = 'DVWASession'
core.new_session(name=sessionName, overwrite=True, apikey=apiKey)
contextName = "DVWA"
contextId = zap.context.new_context(contextName, apiKey)
contextIncludeURL = ['http://127.0.0.1:85.*']
for url in contextIncludeURL:
        pprint(url + ' -> ' +
                zap.context.include_in_context(contextname=contextName,
                                           regex=url, apikey=apiKey))
for url in contextExcludeURL:
        pprint(url + ' -> ' +
                zap.context.exclude_from_context(contextname=contextName,
                                             regex=url, apikey=apiKey))


sessionManagement = 'cookieBasedSessionManagement'

zap.sessionManagement.set_session_management_method(
                contextid=contextId, methodname=sessionManagement,
                methodconfigparams=None, apikey=apiKey)

authMethod = 'scriptBasedAuthentication'
authScriptName = 'auths'
authScriptEngine = 'Oracle Nashorn'
authScriptFileName = '/root/GitHub/DVWA/auths.js'
authScriptDescription = 'This is a description'

# Define an authentication method with parameters for the context
auth = zap.authentication
authParams = ('scriptName=auths&'
              'LoginURL=http://localhost:85/login.php&'
              #'RedirectURL=http://localhost:85/index.php&'
              'CSRFField=user_token&'
              'POSTData=username%3D%7B%25username%25%7D%26password%3D%7B%25password%25%7D%26Login%3DLogin%26user_token%3D%7B%25user_token%25%7D'
              )

script = zap.script
script.remove(scriptname=authScriptName, apikey=apiKey)

script.load(scriptname=authScriptName,
                            scripttype='authentication',
                            scriptengine=authScriptEngine,
                            filename=authScriptFileName,
                            scriptdescription=authScriptDescription,
                            apikey=apiKey)

pprint('Set authentication method: ' + authMethod + ' -> ' +
            auth.set_authentication_method(contextid=contextId,
                                           authmethodname=authMethod,
                                           authmethodconfigparams=authParams,
                                           apikey=apiKey))
loginindicator = '\Q<a href="logout.php">Logout</a>\E'
logouticator = '(?:Location: [./]*login\.php)|(?:\Q<form action="login.php" method="post">\E)'
auth.set_logged_in_indicator(contextid=contextId,
                                        loggedinindicatorregex=loginindicator,
                                        apikey=apiKey)
auth.set_logged_out_indicator(contextid=contextId,
                                        loggedoutindicatorregex=logouticator,
                                        apikey=apiKey)
userIdList = []
userList = [
    {'name': 'Admin', 'credentials': 'Username=admin&Password=password'}
]

users = zap.users
for user in userList:
    userName = user.get('name')
    print('Create user ' + userName + ':')
    userId = users.new_user(contextid=contextId, name=userName,
                        apikey=apiKey)
    userIdList.append(userId)
    pprint('User ID: ' + userId + '; username -> ' +
                        users.set_user_name(contextid=contextId, userid=userId,
                                            name=userName, apikey=apiKey) +
                        '; credentials -> ' +
        users.set_authentication_credentials(contextid=contextId,
                            userid=userId,
                            authcredentialsconfigparams=user.get('credentials'),
                            apikey=apiKey) +
                        '; enabled -> ' +
        users.set_user_enabled(contextid=contextId, userid=userId,
                                            enabled=True, apikey=apiKey))



zap.pscan.enable_all_scanners(apikey=apiKey)
ascan = zap.ascan

print 'Accessing target %s' % target
# try have a unique enough session...

core.access_url(url=target, followredirects=True, apikey=apiKey)
for url in applicationURL:
    pprint('Access URL ' + url)
    core.access_url(url=url, followredirects=True, apikey=apiKey)
# Give the sites tree a chance to get updated
time.sleep(2)
scanPolicyName = None
forcedUser = zap.forcedUser
spider = zap.spider
ajax = zap.ajaxSpider
scanId = 0

for userId in userIdList:
        print('Starting scans with User ID: ' + userId)

        # Spider the target and recursively scan every site node found
        
        scanId = spider.scan_as_user(contextid=contextId, userid=userId,
                url=target, maxchildren=None, recurse=True, subtreeonly=None,
                apikey=apiKey)
        
        print('Start Spider scan with user ID: ' + userId +
                '. Scan ID equals: ' + scanId)
        # Give the spider a chance to start
        
        time.sleep(2)
        try:
            while (int(spider.status(scanId)) < 100):
                print('Spider progress: ' + spider.status(scanId) + '%')
                time.sleep(2)
        except:
            print ('Error in int :' + (spider.status(scanId)))
       
        print('Spider scan for user ID ' + userId + ' completed')
        
        
        scanId = ascan.scan_as_user(url=target, contextid=contextId,
                userid=userId, recurse=True, scanpolicyname=scanPolicyName,
                method=None, postdata=True, apikey=apiKey)
        print('Start Active Scan with user ID: ' + userId +
                '. Scan ID equals: ' + scanId)
        # Give the scanner a chance to start

        time.sleep(2)
        while (int(ascan.status(scanId)) < 100):
            print('Active Scan progress: ' + ascan.status(scanId) + '%')
            time.sleep(2)
        print('Active Scan for user ID ' + userId + ' completed')
# Give the passive scanner a chance to finish
time.sleep(5)
'''
# Report the results
print 'Hosts: ' + ', '.join(zap.core.hosts)
print 'Alerts: '
pprint (zap.core.alerts())
'''

Simon Bennetts

unread,
May 19, 2017, 9:22:26 AM5/19/17
to OWASP ZAP User Group
If you're having problems with the API then I always recommend switching to the UI so that you can see whats going wrong.
In this case I'd strongly recommend following the FAQ that Kingthorin pointed you to.
If that doesnt work for you then let us know at what point you are stuck and what appears to be happening.
Once that works for you then try to translate that into API calls.

Cheers,

Simon
Reply all
Reply to author
Forward
0 new messages