Overview

browserfuzz

A very simple browser fuzzer based on tornado.

Licence

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Usage

usage: browserfuzz.py [-h] [-b BROWSER] [-p PAYLOADS [PAYLOADS ...]]
                      [-c CONTENT [CONTENT ...]] [-d DELAY]

A simple extendible browser fuzzing framework

optional arguments:
  -h, --help            show this help message and exit
  -b BROWSER, --browser BROWSER
                        Launch default browser
  -p PAYLOADS [PAYLOADS ...], --payloads PAYLOADS [PAYLOADS ...]
                        Payloads to be used
  -c CONTENT [CONTENT ...], --content CONTENT [CONTENT ...]
                        List of files to be used as fuzzing content
  -d DELAY, --delay DELAY
                        Delay in ms to refresh

Example usage

python browserfuzz.py -c script/base64image.html

scripts/base64image.html contains:

<img alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAIAAABvrngfAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAQSURBVBhXY/iPAWgq9P8/APW/a5VZY0yYAAAAAElFTkSuQmCC" />

This script will be converted to:

<html><body onload="setTimeout(function(){document.location='/Insert?r=4905230007';}, 300);"><img alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAIAAABvrngfAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAQSURBVBhXY/iPAWgq9P8/APW/a5VZY0yYAAAAAElFTkSuQmCC" /></body></html>

and at each iteration the <img> tag is fuzzed, for example:

<html><body onload="setTimeout(function(){document.location='/Insert?r=4905230007';}, 300);">AAAAimg alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAIAAABvrngfAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAQSURBVBhXY/iPAWgq9P8/APW/a5VZY0yYAAAAAElFTkSuQmCC" /></body></html>

In order to start the fuzzing process, open the target browser and point it to http://localhost:8888. You can open as many tabs/windows as required and point to the same URL.

You can also launch the default browser by using the -b x parameter. x determines how many instances / windows are launched.

python browserfuzz.py -b 10 -c scripts/base64image.html

This will launch 10 instances of the default browser.

The payloads that are used can also be defined via the command line parameter -p.

python browserfuzz.py -c scripts\base64image.html -p "'AAAA'" -b 1

NOTE that the string inside the " is a Python value, so a string should be surrounded by '.

python browserfuzz.py -c scripts\base64image.html -p "'' * 1024" -b 1

is also valid

-p can take multiple values such as:

python browserfuzz.py -c scripts\base64image.html -p "'A'" "'B'" -b 1

-p can also have file references. If the file is found, then each of its line is evaluated and added to the payload list

python browserfuzz.py -c scripts\base64image.html -p payloads/misc.txt

where misc.txt contains:

'A' * 1024
'B' * 1024

Extending

Creating a new fuzzer

The first thing to do is extend the class FuzzHandler. The classes available with browserfuzz will be used here as examples of usage.

class SwapMutationHandler(FuzzHandler):

When the fuzz plugin is initialized,a dictionary named data is passed to the method initialize. This is an example of an overloaded initialize method.

def initialize(self, data):
    self.data = data
    self.use_payloads = False

The data dictionary contains the following default properties:

data['count']

data['content']
data['contentcount']

data['methods']
data['methodcount']

data['payloads']
data['payloadcount']

data['result']

data['count']

data['count'] holds the position of the current token being fuzzed. By default this counter is a character position of the content being fuzzer. However this can be changed to reflect other values. This is best achieved by overloading the method get_next_token()

data['content'] and data['contentcount']

data['content'] is a list of text strings which determine the content to be used during fuzzing. This list is populated by files inside the folder scripts/ or by a list of filenames provided by the user via the command line.

data['contentcount'] indicates which string element in data['content'] is being used as fuzzing content.

data['methods'] and data['methodscount']

data['methods'] holds a list of tuples (A, B) where A is a string and B is a class. These map a URL to a fuzzing plugin.

For example:

methods = [
           ('Insert', InsertMutationHandler),
           ('ReplaceNumber', ReplaceNumberMutationHandler),
           ('Swap', SwapMutationHandler),
           ('Repeat', RepeatMutationHandler),
]

means that references to the URL /Insert will use the InsertMutationHandler class, /ReplaceNumber will use ReplaceNumberMutationHandler, etc.

data['methodcounter'] holds the index of the current method being used during a fuzzing session.

data['payload'] and data['payloadcount']

data['payload'] holds a list of payloads that will be used during fuzzing. data['payloadcount'] holds the current payload index being used.

data['result']

data['result'] holds a dictionary of results that will be shown at the end of a fuzz session. If you need to keep a report of anything from your fuzzing plugins, add a new key to this dictionary and add your values.

adding your own values

If you need to store data that is persistent to a fuzzing session, add your new key to the data dictionary. For example, the class RepeatMutationHandler initializes a list of numerical items as follows:

def initialize(self, data):
    self.data = data
    self.use_payloads = True

    if 'regex_%s' % self.data['contentcount'] not in self.data:
        self.data['regex_%s' % self.data['contentcount']] = None

get_next_token()

The default functionality of this method is to increment data['count'] by 1 and throws a NoMoreToken exception when no more data['count'] is equal to the length of the current data['content'] string.

This method can be overridden to count other tokenization schemes that the fuzzer might use (see code of ReplaceNumber fuzzer)

get_next_payload()

The default functionality of this method is to increment data['payloadcount'] by 1 and throws a NoMorePayloads exception when no more payloads are available.

This method can be overriden. For example the fuzzing class SwapMutationHandler does not use the actual payload values, so its get_next_payload() is implemented as:

def get_next_payload(self):
    raise NoMorePayloads()

get_next_method()

This method increments the value of data['methodcount'] by 1 and throws a NoMoreMethods exception when no more methods are available.

This method can be overriden however this might break the way methods are executed in order. Override with care!