Skip to content

Writing New WAF Checks

Pinaki Mondal edited this page Oct 27, 2019 · 1 revision

Writing a New Plugin

  1. Create a new python script in the plugins directory with the short-name of the check (e.g. wafname.py).
  2. Use the template below to get started.
  3. Make modifications within the is_waf method so that it returns True if the WAF is detected, otherwise return False.
  4. Test and test some more.
  5. Once happy with the results, you are welcome to submit a pull on GitHub.

That's all!

Simple template for getting started

#!/usr/bin/env python
'''
Copyright (C) 2019, WAFW00F Developers.
See the LICENSE file for copying permission.
'''

NAME = 'WAF Name'


def is_waf(self):
    schemes = [
        # This part contains the checks needed to identify the WAF
        self.matchHeader(('Header Field', 'Header Key')),
        self.matchReason(503),
        self.matchContent(r"Regex for identifying the WAF")
    ]
    # Use any() if any of the checks can identify the WAF.
    # Use all() if all the checks are needed to identify the WAF.
    if any(i for i in schemes): # Replace any with all(i for i in schemes)
        return True
    return False

Tips

The following tips should help you in getting quickly up to scratch with adding new checks:

  1. Check the code in the plugins directory, some checks are very simple, others a bit more complex.
  2. Sometimes, WAFs will simply reveal themselves all the time through some HTTP header.
  3. Other times, they will show themselves only when certain rules are triggered by the "attacks".
  4. Note that each HTTP request and response is cached so that the tool does not emit a huge amount of traffic.
  5. Group some fingerprints under diff. schemas to write the plugins if they don't match the any() or all() criteria.

Exposed methods for your checks

When creating new checks, you will normally want to make use of one of the following methods:

  • self.matchHeader(self, headermatch, attack=False)
    Where headermatch is a tuple consisting of the header name (case insensitive) and a regular expression to match the value of the header, e.g. ('someheader','^SuperWAF[a-fA-F0-9]$'). If attack is set to True, the matchHeader does not send the normal HTTP request, but instead sends a series of attack requests.
  • self.matchStatus(self, statuscode, attack=True)
    Where statuscode is an integer value containing the status code returned for a simple malicious request. Setting the attack=False will return the instance of a normal request.
  • self.matchReason(self, reasoncode, attack=True)
    Where reasoncode is an string value containing the response phrase/reason to be matched with the code returned for a simple malicious request. Setting the attack=False will return the instance of a normal request.
  • self.matchContent(self, regex, attack=True)
    Where regex is an regular expression value containing the regex to be matched within the content returned for a malicious request. Setting the attack=False will return the instance of a normal request.
  • self.matchCookie(match)
    Which is a shortcut for self.matchHeader(('set-cookie', match)).

Note: self.Request and methods based on it (e.g. self.normalrequest() and functions in attacks) makes use of requests library.

WAFW00F Wiki Index

Clone this wiki locally