Welcome to a series of blog posts aimed at helping you “hack the ZAP source
code”.
The previous post in this series is: Hacking ZAP #2 - Getting Started
One of the easiest ways to enhance ZAP is to write new passive scan rules.
Passive scan rules are used to warn the user of potential vulnerabilities that can be detected passively - they are not allowed to make any new
requests or manipulate the requests or responses in any way.
They typically run against all of the requests and responses that flow through ZAP.
Passive rules run in separate background thread so that they have as little effect on performance as possible.
You can write passive scan rules dynamically using scripts, as we will see later in this series, but even then it’s very useful to understand some of the concepts and the underlying classes available to you.
Where to start
The easiest way to get started is to rip off an existing rule. The passive scan rules can be found in 3 add-ons in the zap-extensions project, depending on their status:
- Release status: addOns/pscanrules
- Beta status: addOns/pscanrulesBeta
- Alpha status: addOns/pscanrulesAlpha
There are also some simple examples that we will examine in more detail. These are all in the pscanrulesAlpha
add-on.
The main classes
The following classes are key to implementing passive scan rules.
PluginPassiveScanner
- this is
the class that all passive rules must extend. There are 2 key methods that you will need to implement:
scanHttpRequestSend(HttpMessage msg, int id)
- This is called for every request. All details of the request are available via themsg
parameter, as detailed below.scanHttpResponseReceive(HttpMessage msg, int id, Source source)
- This is called for every response. All details of the request and response are available via themsg
parameter, as detailed below. The response is also available as a DOM structure via thesource
parameter.
You can implement one or both of these methods depending on your requirements. You can examine any part of the request and response in order to find potential vulnerabilities, but you must not change anything.
If you find a potential vulnerability then you can raise it via the method: PassiveScanThread.raiseAlert(int id, Alert alert)
An HttpMessage
is passed in to both of the
scan
methods. This class has methods that allow you to access all aspects of the request and response, although the latter is obviously only
available in scanHttpResponseReceive
. Some examples include:
msg.getRequestHeader().getMethod()
msg.getRequestHeader().getHttpCookies()
msg.getRequestHeader().getHeaders()
msg.getRequestHeader().getContentLength()
msg.getRequestBody().toString()
msg.getResponseHeader().getHeaders()
msg.getResponseHeader().getStatusCode()
msg.getResponseBody().toString()
A Source
parameter is passed into scanHttpResponseReceive
-
this is a DOM representation of the response generated by the Jericho HTML parser. See the Jericho
documentation or the other scan rules for examples of how to access DOM elements.
The Alert
class is used to represent
potential vulnerabilities. It supports the following fields:
pluginId
Used to identify the scanner, especially useful via the ZAP APIname
The summary displayed to the userrisk
An indication of how serious the issue is:Alert.RISK_INFO
Informational (it’s not really a vulnerability)Alert.RISK_LOW
A low risk vulnerabilityAlert.RISK_MEDIUM
A medium risk vulnerabilityAlert.RISK_HIGH
A high risk vulnerability
confidence
An indication of how likely this is a real problem:Alert.CONFIDENCE_FALSE_POSITIVE
Should not be used - this is for the user to setAlert.CONFIDENCE_LOW
A lower level of confidenceAlert.CONFIDENCE_MEDIUM
A medium level of confidenceAlert.CONFIDENCE_HIGH
A higher level of confidenceAlert.CONFIDENCE_USER_CONFIRMED
Should not be used - this is for the user to set
description
A more detailed descriptionuri
The URI affectedparam
The name of the vulnerable parameter, if relevantattack
The attack string used (not relevant for passive vulnerabilities)otherInfo
Information that doesn’t readily fit into any of the other fieldssolution
Information about how to prevent the vulnerabilityreference
A list of URLs giving more information about this type of vulnerability (separated by newline characters)evidence
A string present in the request or response which can be used as evidence of the vulnerability - this will be highlighted when the related request or response is displayedcweId
The CWE idwascId
The WASC Threat Classification id
Simple example
The ExampleSimplePassiveScanRule
class implements a very simple passive
scan rule. As you will see, it just raises an alert randomly, so it isn’t of any practical use. However it does demonstrate a couple of useful
features:
It uses the Vulnerabilities
class to get the
name, description, solution and references. This class loads vulnerability details from the
vulnerabilities.xml
files included with ZAP. There are actually
a set of vulnerabilities.xml
files as it is internationalized, so ZAP will read the localized version for the language the user has selected,
defaulting back to English for any phrases that have not been translated. This is therefore a quick and easy way to fill in these details, as
long as the relevant vulnerability is included in that file.
It also uses the Log4j 2 Logger
class to output debug messages. This is the recommended way of outputting such
messages.
Note that the pluginId
needs to be unique across all active and passive scan rules. The master list of ids is in the
scanners.md file.
File based example
The ExampleFilePassiveScanRule
class implements a
slightly more complex passive scan rule. In this case it reads in a set of strings from a configuration file and checks for their presence in
the response. It could also use hardcoded strings, but the advantage of the approach taken is that a knowledgeable user could manually edit the
file to meet their requirement.
(ZAP automatically extracts the files located in the zapHomeFiles
directory into a directory underneath the ZAP user directory.)
This class also demonstrates a couple of other features:
Instead of using the Vulnerabilities
class the code uses Constant.messages.getString(str)
. All of the strings used in this way are defined in the
Messages.properties file. If you are just implementing the rule
for your own benefit then you can hardcode the strings if you want, but internationalizing them is very simple and saves having to go back and
change your code if you want to have your rule included in the ZAP Marketplace.
The code also makes use of the getAlertThreshold()
method. This returns an AlertThreshold
which indicates how strictly you should check for
vulnerabilities. The threshold returned can be one of:
LOW
: This indicates you should report more potential vulnerabilities, which might mean more false positivesMEDIUM
: This is the default levelHIGH
: This indicates you should report fewer potential vulnerabilities, which might mean more false negatives
You do not have to use the threshold - especially as it might not be relevant for the vulnerability you are testing for, but it is also a useful way for the user to tune how the rules work and so it’s worth using if you can.
Building and deploying
The alpha passive active scan rules add-on build file is addOns/pscanrulesAlpha/pscanrulesAlpha.gradle.kts. All you need to do is run the Gradle task :addOns:pscanrulesAlpha:copyZapAddOn
in the zap-extensions
project and the relevant add-on will be built and copied to the correct location, assuming you have a ZAP core project called zaproxy
. If you want to deploy to a different location then you can use the command line argument --into=/path/to/copy/into/
.
Updating the help
To finish off a new rule you should add a short description of the rule to the help file: pscanalpha.html
This is not really necessary unless you want to publish your rules.
A future post will cover how to contribute your code back to the ZAP community and progress it from alpha to beta and then release status.