org.owasp.jbrofuzz.core.Verifier.java Source code

Java tutorial

Introduction

Here is the source code for org.owasp.jbrofuzz.core.Verifier.java

Source

/**
 * JbroFuzz 2.5
 *
 * JBroFuzz - A stateless network protocol fuzzer for web applications.
 * 
 * Copyright (C) 2007 - 2010 subere@uncon.org
 * This file is part of JBroFuzz.
 * 
 * JBroFuzz 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.
 * 
 * JBroFuzz 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 JBroFuzz.  If not, see <http://www.gnu.org/licenses/>.
 * Alternatively, write to the Free Software Foundation, Inc., 51 
 * Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 * 
 * Verbatim copying and distribution of this entire program file is 
 * permitted in any medium without royalty provided this notice 
 * is preserved. 
 * 
 */
package org.owasp.jbrofuzz.core;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.CharUtils;
import org.apache.commons.lang.StringUtils;
import org.owasp.jbrofuzz.system.Logger;
import org.owasp.jbrofuzz.util.JBroFuzzFileFilter;

/**
 * <p><code>Verifier</code> checks and loads the 
 * fuzzers from file.</p>
 * 
 * <p>The structure of this code is inspired from 
 * Elliotte Rusty Harold, XOM nu.xom.Verifier 
 * class, without the nice optimizations</p>
 * 
 * @author subere@uncon.org
 * @version 2.0
 * @since 2.0
 * 
 */
public final class Verifier {

    private Verifier() {
    }

    // The maximum number of chars to be read from file, regardless
    private static final int MAX_CHARS = Character.MAX_VALUE;
    // The maximum number of lines allowed to be read from the file
    private static final int MAX_LINES = 500000;
    // The maximum length of a line allowed
    private static final int MAX_LINE_LENGTH = 2048;

    // The maximum name length for a prototype
    private static final int MAX_PROTO_NAME_LENGTH = Byte.MAX_VALUE;
    // The maximum number of payloads in a prototype
    private static final int MAX_NO_OF_PAYLOADS = 500000;
    // The maximum number of categories of a prototype
    private static final int MAX_NO_OF_CATEGORIES = Byte.MAX_VALUE;

    private static final String ERROR_MSG = "\n\n\tBroken JBroFuzz Installation:\n\n\t";

    private static Map<String, Prototype> prototypes, headers = null;

    /**
     * <p>
     * Method called from the Database constructor to load the
     * fuzzers from file.
     * </p>
     * 
     * <p>This method calls the internal method 
     * parseFile(ClassLoader).</p>
     * 
     * @return Map<String, Prototype>
     * 
     * @author subere@uncon.org
     * @version 2.0
     * @since 2.0
     */
    public static Map<String, Prototype> loadFile(String fileName) {

        // Validate string first of all

        if ("fuzzers.jbrf".equalsIgnoreCase(fileName)) {

            //         if (prototypes == null) {

            // Check for the existence of fuzzers.jbrf within the
            // current user directory
            final boolean extFilePresent = checkExternalFile(fileName);
            String inputContents;

            if (extFilePresent) {
                Logger.log("Loading from the external file fuzzers.jbrf found in the current directory", 0);
                inputContents = Verifier.parseExtFile(fileName);

            } else {

                Logger.log("Loading from the internal file fuzzers.jbrf found in the current directory", 0);
                inputContents = Verifier.parseFile(fileName);

            }

            prototypes = new HashMap<String, Prototype>();

            Verifier.parsePrototypes(prototypes, inputContents);
            Logger.log("fuzzers.jbrf file loaded with " + prototypes.size() + " fuzzers", 0);
            return prototypes;

        } else if ("headers.jbrf".equalsIgnoreCase(fileName)) {

            //         if (headers == null) {
            final String headerContents = Verifier.parseFile(fileName);

            headers = new HashMap<String, Prototype>();

            Verifier.parsePrototypes(headers, headerContents);
            return headers;

        } else {

            throw new RuntimeException(ERROR_MSG + "is not a valid name to load " + fileName);

        }

    }

    /**
     * <p>Load any .jbrf file, given as input the absolute path of the file,
     * as a String.</p>
     * 
     * <p>In the event of an error, load the default build-in fuzzers.jbrf 
     * file, using the method loadFile().</p>
     * 
     * @param fuzzersFilePath The absolute path of the .jbrf file
     * 
     * @return
     */
    public static Map<String, Prototype> loadAnyFile(final String fuzzersFilePath) {

        if (fuzzersFilePath.length() > 512) {
            Logger.log("Cannot open a .jbrf file that has an absolute path greater than 512 characters", 4);
            return loadFile("fuzzers.jbrf");
        }

        if (!fuzzersFilePath.endsWith(".jbrf")) {
            Logger.log("Cannot open file, does not have a .jbrf extension", 4);
            return loadFile("fuzzers.jbrf");
        }

        final boolean extFilePresent = checkExternalFilePath(fuzzersFilePath);
        String inputContents;

        if (extFilePresent) {

            inputContents = Verifier.parseExtFilePath(fuzzersFilePath);

        } else {

            inputContents = Verifier.parseFile("fuzzers.jbrf");

        }

        prototypes = new HashMap<String, Prototype>();

        Verifier.parsePrototypes(prototypes, inputContents);
        Logger.log("External file loaded with " + prototypes.size() + " fuzzers", 0);
        return prototypes;
    }

    /**
     * <p>Checks for the presence of an external file within the 
     * current directory.</p>
     * 
     * @param fileName
     * @return   true if the file is present and can be read, false
     *          otherwise.
     * 
     * @author daemonmidi@gmail.com
     * @version 2.4
     * @since 2.4
     *
     * @author subere@uncon.org
     * @version 2.3
     * @since 2.1
     *
     *
     */
    private static boolean checkExternalFile(String fileName) {

        String dirString;
        try {

            dirString = System.getProperty("user.dir");

        } catch (final SecurityException e) {

            return false;

        }
        final File inputFile = new File(dirString + File.separator + fileName);
        JBroFuzzFileFilter jbfff = new JBroFuzzFileFilter();
        if (!jbfff.accept(inputFile)) {
            return false;
        }

        if (inputFile.exists()) {

            if (inputFile.isDirectory()) {

                return false;
            }
            if (!inputFile.canRead()) {

                return false;

            }

            return true;

        } else {

            return false;

        }

    }

    /**
     * <p>Checks for the presence of an external file within any 
     * directory.</p>
     * 
     * @param fileNamePath The absolute path of the file
     * 
     * @return   true if the file is present and can be read, false
     *          otherwise.
     * 
     * @author subere@uncon.org
     * @version 2.1
     * @since 2.1
     */
    private static boolean checkExternalFilePath(String fileNamePath) {

        final File inputFile = new File(fileNamePath);

        if (inputFile.exists()) {

            if (inputFile.isDirectory()) {

                return false;
            }
            if (!inputFile.canRead()) {

                return false;

            }

            return true;

        } else {

            return false;

        }

    }

    /**
     * <p>Return the contents of an internal file a String.</p>
     *  
     * @param fileName e.g. fuzzers.jbrf; headers.jbrf
     * @return the contents of the file as a String
     * 
     * @author subere@uncon.org
     * @version 2.1
     * @since 2.1
     */
    private static String parseExtFile(String fileName) {

        final File inputFile = new File(System.getProperty("user.dir") + File.separator + fileName);

        JBroFuzzFileFilter jbfff = new JBroFuzzFileFilter();
        if (!jbfff.accept(inputFile)) {
            return "This file is not accepted";
        }

        if (inputFile.exists()) {
            if (inputFile.isDirectory()) {

                return "File is a directory:\n\n" + fileName;
            }
            if (!inputFile.canRead()) {

                return "File cannot be read:\n\n" + fileName;

            }
        } else {

            return "File does not exist:\n\n" + fileName;

        }

        int counter = 0;
        InputStream in = null;
        FileInputStream fis = null;
        final StringBuffer fileContents = new StringBuffer();
        try {
            fis = new FileInputStream(inputFile);
            in = new BufferedInputStream(fis);

            int c;
            // Read, having as upper maximum the int maximum
            while (((c = in.read()) > 0) && (counter <= MAX_CHARS)) {
                // Allow the character only if its printable ascii or \n
                if ((CharUtils.isAsciiPrintable((char) c)) || (((char) c) == '\n')) {
                    fileContents.append((char) c);
                }
                counter++;

            }

            in.close();
            fis.close();

        } catch (final IOException e) {

            return "Attempting to open the file caused an I/O Error:\n\n" + fileName;

        } finally {
            IOUtils.closeQuietly(in);
            IOUtils.closeQuietly(fis);
        }

        if (counter == MAX_CHARS) {
            final String maxMessage = "\n... stopped reading file after " + MAX_CHARS + " characters.\n";
            fileContents.append(maxMessage);
            Logger.log(maxMessage, 3);
        }
        return fileContents.toString();

    }

    /**
     * <p>Return the contents of any .jbrf file, given the
     * file's absolute path.</p>
     *  
     * @param fuzzersFilePath The absolute path pointing to
     * a .jbrf file
     * @return the contents of the file as a String
     * 
     * @author subere@uncon.org
     * @version 2.1
     * @since 2.1
     */
    private static String parseExtFilePath(String fuzzersFilePath) {

        final File inputFile = new File(fuzzersFilePath);

        if (inputFile.exists()) {
            if (inputFile.isDirectory()) {

                return "File is a directory:\n\n" + fuzzersFilePath;
            }
            if (!inputFile.canRead()) {

                return "File cannot be read:\n\n" + fuzzersFilePath;

            }
        } else {

            return "File does not exist:\n\n" + fuzzersFilePath;

        }

        int counter = 0;
        InputStream in = null;
        FileInputStream fis = null;
        final StringBuffer fileContents = new StringBuffer();
        try {
            fis = new FileInputStream(inputFile);
            in = new BufferedInputStream(fis);

            int c;
            // Read, having as upper maximum the int maximum
            while (((c = in.read()) > 0) && (counter <= MAX_CHARS)) {
                // Allow the character only if its printable ascii or \n
                if ((CharUtils.isAsciiPrintable((char) c)) || (((char) c) == '\n')) {
                    fileContents.append((char) c);
                }
                counter++;

            }

            in.close();
            fis.close();

        } catch (final IOException e) {

            return "Attempting to open the file caused an I/O Error:\n\n" + fuzzersFilePath;

        } finally {
            IOUtils.closeQuietly(in);
            IOUtils.closeQuietly(fis);
        }

        if (counter == MAX_CHARS) {
            final String maxMessage = "\n... stopped reading file after " + MAX_CHARS + " characters.\n";
            fileContents.append(maxMessage);
            Logger.log(maxMessage, 3);
        }
        return fileContents.toString();

    }

    /**
     * <p>Return the contents of an internal file a String.</p>
     *  
     * @param fileName e.g. fuzzers.jbrf; headers.jbrf
     * @return the contents of the file as a String
     * 
     * @author subere@uncon.org
     * @version 2.0
     * @since 2.0
     */
    private static String parseFile(String fileName) {

        final StringBuffer fileContents = new StringBuffer();

        // Attempt to read from the jar file
        final URL fileURL = ClassLoader.getSystemClassLoader().getResource(fileName);

        if (fileURL == null) {
            throw new RuntimeException(ERROR_MSG + "could not find " + fileName);
        }

        // Read the characters from the file
        BufferedReader in = null;
        try {
            final URLConnection connection = fileURL.openConnection();
            connection.connect();

            in = new BufferedReader(new InputStreamReader(connection.getInputStream()));

            int counter = 0;
            int c;
            while (((c = in.read()) > 0) && (counter < MAX_CHARS)) {
                // Allow the character only if its printable ascii or \n
                if ((CharUtils.isAsciiPrintable((char) c)) || (((char) c) == '\n')) {
                    fileContents.append((char) c);
                }
                counter++;
            }
            in.close();

            if (counter == MAX_CHARS) {

                throw new RuntimeException(ERROR_MSG + "\n... stopped reading file :" + fileName + "\nafter "
                        + MAX_CHARS + " characters.\n\n");

            }

        } catch (final IOException e1) {
            throw new RuntimeException(ERROR_MSG + "could not read " + fileName);
        } finally {
            IOUtils.closeQuietly(in);
        }

        return fileContents.toString();
    }

    /**
     * <p>Method responsible for parsing the printable
     * String contents of the file fuzzers.jbrf to
     * the prototype HashMap.</p>
     * 
     * @param input the .jbrf file in String input
     */
    private static void parsePrototypes(Map<String, Prototype> map, String input) {

        // Break down the file contents into lines
        final String[] fileInput = input.split("\n");

        if (fileInput.length > MAX_LINES) {
            throw new RuntimeException(ERROR_MSG + "fuzzers.jbrf has more than " + MAX_LINES + " lines.");
        }

        if (fileInput.length < 3) {
            throw new RuntimeException(ERROR_MSG + "fuzzers.jbrf does not have enough lines.");
        }

        for (int i = 0; i < fileInput.length; i++) {

            // Ignore comment lines starting with '#'
            if (fileInput[i].startsWith("#")) {
                continue;
            }

            // Ignore lines of length greater than MAX_LINE_LENGTH
            if (fileInput[i].length() > MAX_LINE_LENGTH) {
                continue;
            }

            // Check 1 indicating a likely prototype candidate
            try {
                if (fileInput[i].charAt(1) != ':') {
                    continue;
                }
                if (fileInput[i].charAt(13) != ':') {
                    continue;
                }

            } catch (final IndexOutOfBoundsException e1) {
                continue;
            }

            // [0] -> P || R || X
            // [1] -> "001-HTT-MTH"
            // [2] -> Uppercase HTTP Methods
            // [3] -> 8
            final String[] _fla = fileInput[i].split(":");

            // Check that there are four fields separated by :
            if (_fla.length != 4) {
                continue;
            }

            final char inputTypeChar = _fla[0].charAt(0);

            // Check [0] -> Fuzzer Type 'Z' or 'P', etc..
            if (!Prototype.isValidFuzzerType(inputTypeChar)) {
                continue;
            }

            // The Id: 009-SQL-INJ cannot be empty
            if (_fla[1].isEmpty()) {
                continue;
            }

            // The name: "SQL Injection" cannot be empty
            if (_fla[2].isEmpty()) {
                continue;
            }

            // Check the prototype name length
            if (_fla[2].length() > MAX_PROTO_NAME_LENGTH) {
                continue;
            }

            int noPayloads = 0;
            try {
                noPayloads = Integer.parseInt(_fla[3]);

            } catch (final NumberFormatException e) {
                continue;
            }

            // Check how many payloads this prototype has
            if (noPayloads > MAX_NO_OF_PAYLOADS) {
                continue;
            }

            // Allow only zero fuzzers to have no payloads
            if (noPayloads == 0) {
                continue;
            }

            // Check we have that many payloads left in file
            if (i + noPayloads > fileInput.length) {
                continue;
            }

            try {
                if (!fileInput[i + 1].startsWith(">")) {
                    continue;
                }
                if (!fileInput[i + 2].startsWith(">>")) {
                    continue;
                }
            } catch (final IndexOutOfBoundsException e) {
                continue;
            }

            String line2 = "";
            try {
                line2 = fileInput[i + 1].substring(1);
            } catch (final IndexOutOfBoundsException e) {
                continue;
            }

            String comment = "";
            try {
                comment = fileInput[i + 2].substring(2);
            } catch (final IndexOutOfBoundsException e) {
                continue;
            }

            // [0] -> HTTP Methods
            // [1] -> Replacive Fuzzers
            // [2] -> Uppercase Fuzzers
            final String[] _sla = line2.split("\\|");
            if (_sla.length > MAX_NO_OF_CATEGORIES) {
                continue;
            }

            // Alas! Finally create a prototype
            final Prototype proto = new Prototype(inputTypeChar, _fla[1], _fla[2]);

            // If categories do exist in the second line
            if (_sla.length > 0) {

                for (String categ_ry : _sla) {
                    // add the category to the prototype
                    categ_ry = StringUtils.stripEnd(categ_ry, " ");
                    categ_ry = StringUtils.stripStart(categ_ry, " ");

                    if (!categ_ry.isEmpty()) {
                        proto.addCategory(categ_ry);
                    }

                }
            }
            // If no categories have been identified,
            // add a default category
            else {

                proto.addCategory("JBroFuzz");

            }

            // Add the comment
            proto.addComment(comment);

            // Add the values of each payload
            for (int j = 1; j <= noPayloads; j++) {
                try {

                    proto.addPayload(fileInput[i + 2 + j]);

                } catch (final IndexOutOfBoundsException e) {
                    continue;
                }
            }

            // Finally add the prototype to the database
            map.put(_fla[1], proto);

        }

    }

}