/***********************************************************************
*
* $CVSHeader$
*
* This file is part of WebScarab, an Open Web Application Security
* Project utility. For details, please see http://www.owasp.org/
*
* Copyright (c) 2002 - 2004 Rogan Dawes
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Getting Source
* ==============
*
* Source for this application is maintained at Sourceforge.net, a
* repository for free software projects.
*
* For details, please see http://www.sourceforge.net/projects/owasp
*
*/
/*
* $Id: Fuzzer.java,v 1.8 2005/09/21 22:06:33 rogan Exp $
*/
package org.owasp.webscarab.plugin.fuzz;
import org.owasp.webscarab.httpclient.ConversationHandler;
import org.owasp.webscarab.httpclient.FetcherQueue;
import org.owasp.webscarab.model.Preferences;
import org.owasp.webscarab.model.ConversationID;
import org.owasp.webscarab.model.HttpUrl;
import org.owasp.webscarab.model.Request;
import org.owasp.webscarab.model.Response;
import org.owasp.webscarab.model.StoreException;
import org.owasp.webscarab.model.NamedValue;
import org.owasp.webscarab.plugin.Framework;
import org.owasp.webscarab.plugin.Plugin;
import org.owasp.webscarab.plugin.Hook;
import org.owasp.webscarab.util.Encoding;
import java.util.logging.Logger;
import java.net.URL;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.net.MalformedURLException;
public class Fuzzer implements Plugin, ConversationHandler {
private FuzzerModel _model = null;
private Framework _framework = null;
private FuzzFactory _fuzzFactory = new FuzzFactory();
private FetcherQueue _fetcherQueue = null;
private int _threads = 4;
private Logger _logger = Logger.getLogger(getClass().getName());
public Fuzzer(Framework framework) {
_framework = framework;
_model = new FuzzerModel(_framework.getModel());
loadFuzzStrings();
_fetcherQueue = new FetcherQueue("Fuzzer", this, _threads, 0);
}
private void loadFuzzStrings() {
int i = 0;
String description;
while ((description = Preferences.getPreference("Fuzz." + i + ".description")) != null) {
String location = Preferences.getPreference("Fuzz." + i + ".location");
if (location != null && !description.equals("")) {
try {
URL url = new URL(location);
_fuzzFactory.loadFuzzStrings(description, url.openStream());
} catch (IOException ioe) {
_logger.warning("Error loading \"" + description + "\" from " + location + " : " + ioe.getMessage());
}
}
i++;
}
}
public FuzzFactory getFuzzFactory() {
return _fuzzFactory;
}
public FuzzerModel getModel() {
return _model;
}
/** The plugin name
* @return The name of the plugin
*
*/
public String getPluginName() {
return new String("Fuzzer");
}
public void run() {
_model.setStatus("Started");
_model.setStopping(false);
_model.setRunning(true);
while (!_model.isStopping()) {
// queue them as fast as they come, sleep a bit otherwise
boolean submittedRequest = queueRequests();
if (!submittedRequest) {
try {
Thread.sleep(100);
} catch (InterruptedException ie) {}
}
}
_fetcherQueue.clearRequestQueue();
_model.setRunning(false);
_model.setStatus("Stopped");
}
public void startFuzzing() {
int count = _model.getFuzzParameterCount();
if (count>0 && _model.getFuzzUrl() != null) {
_model.setBusyFuzzing(true);
} else {
_logger.warning("Can't fuzz if there are no parameters or URL");
}
}
private Request constructCurrentFuzzRequest() throws MalformedURLException {
Request request = new Request();
request.setMethod(_model.getFuzzMethod());
request.setVersion(_model.getFuzzVersion());
int count = _model.getFuzzHeaderCount();
// _logger.info("Got headers: " + count);
for (int i=0; i<count; i++) {
// _logger.info("Header is " + _model.getFuzzHeader(i));
request.addHeader(_model.getFuzzHeader(i));
}
// if (request.getMethod().equals("POST")) {
// request.setHeader("Content-Type", "application/x-www-form-urlencoded");
// }
String url = _model.getFuzzUrl().toString();
String path = null;
String fragment = null;
String query = null;
String cookie = null;
ByteArrayOutputStream content = null;
count = _model.getFuzzParameterCount();
for (int i=0; i<count; i++) {
Parameter parameter = _model.getFuzzParameter(i);
Object value = _model.getFuzzParameterValue(i);
String location = parameter.getLocation();
if (location.equals(Parameter.LOCATION_PATH)) {
if (path == null) {
path = (String) value;
} else {
path = path + "/" + (value == null ? "" : (String) value);
}
} else if (location.equals(Parameter.LOCATION_FRAGMENT)) {
String frag = parameter.getName();
if (frag == null) {
frag = (String) value;
} else if (value == null) {
frag = frag + "=" + Encoding.urlEncode((String) value);
} else {
frag = null;
}
if (fragment == null) {
fragment = frag;
} else if (frag != null) {
fragment = fragment + "&" + frag;
}
} else if (location.equals(Parameter.LOCATION_QUERY)) {
String q = parameter.getName() + "=" + Encoding.urlEncode((String) value);
if (query == null) {
query = q;
} else {
query = query + "&" + q;
}
} else if (location.equals(Parameter.LOCATION_COOKIE)) {
String c = parameter.getName() + "=" + (String) value;
if (cookie == null) {
cookie = c;
} else {
cookie = cookie + "; " + c;
}
} else if (location.equals(Parameter.LOCATION_BODY)) {
// FIXME - Assumes this is normal form data
String b = parameter.getName() + "=" + Encoding.urlEncode((String) value);
if (content == null) {
content = new ByteArrayOutputStream();
try { content.write(b.getBytes()); }
catch (IOException ioe) {}
} else {
try { content.write(("&"+b).getBytes()); }
catch (IOException ioe) {}
}
} else {
_logger.severe("Skipping unknown parameter location " + location);
}
}
if (path != null) url = url + "/" + path;
if (fragment != null) url = url + ";" + fragment;
if (query != null) url = url + "?" + query;
request.setURL(new HttpUrl(url));
if (cookie != null) request.addHeader("Cookie", cookie);
if (content != null) {
request.setHeader("Content-Length", Integer.toString(content.size()));
request.setContent(content.toByteArray());
} else if (request.getMethod().equals("POST")) {
request.setHeader("Content-Length", "0");
}
return request;
}
public void pauseFuzzing() {
_model.setBusyFuzzing(false);
}
public void stopFuzzing() {
_model.setBusyFuzzing(false);
}
private boolean queueRequests() {
if (!_model.isBusyFuzzing()) return false;
if (_fetcherQueue.getRequestsQueued()>=_threads) return false;
try {
Request request = constructCurrentFuzzRequest();
_fetcherQueue.submit(request);
if (!_model.incrementFuzzer()) {
_model.setBusyFuzzing(false);
}
} catch (Exception e) {
_model.setBusyFuzzing(false);
e.printStackTrace();
return false;
}
return true;
}
public void requestError(Request request, IOException ioe) {
_logger.warning("Caught exception : " + ioe.getMessage());
_model.setBusyFuzzing(false);
}
public void responseReceived(Response response) {
if (response.getStatus().equals("400")) {
_logger.warning("Bad request");
_model.setBusyFuzzing(false);
return;
}
Request request = response.getRequest();
if (request == null) {
_logger.warning("Got a null request from the response!");
return;
}
ConversationID id = _framework.addConversation(request, response, "Fuzzer");
_model.addConversation(id);
}
public boolean stop() {
_model.setStatus("Stopped");
_model.setRunning(false);
return ! _model.isRunning();
}
public void setSession(String type, Object store, String session) throws org.owasp.webscarab.model.StoreException {
}
public void flush() throws StoreException {
}
public boolean isBusy() {
return _model.isBusy();
}
public boolean isRunning() {
return _model.isRunning();
}
public String getStatus() {
return _model.getStatus();
}
public boolean isModified() {
return _model.isModified();
}
public void analyse(ConversationID id, Request request, Response response, String origin) {
Signature signature = new Signature(request);
_model.addSignature(signature);
if (!response.getStatus().equals("304")) {
byte[] content = response.getContent();
if (content == null) content = new byte[0];
String checksum = Encoding.hashMD5(content);
_model.addChecksum(signature.getUrl(), checksum);
}
}
public void loadTemplateFromConversation(ConversationID id) {
if (_model.isBusyFuzzing()) {
stopFuzzing();
}
Request request = _framework.getModel().getRequest(id);
HttpUrl url = request.getURL();
if (url.getParameters()!=null)
url = url.getParentUrl();
_model.setFuzzMethod(request.getMethod());
_model.setFuzzUrl(url.toString());
_model.setFuzzVersion(request.getVersion());
while(_model.getFuzzHeaderCount()>0) {
_model.removeFuzzHeader(0);
}
while(_model.getFuzzParameterCount()>0) {
_model.removeFuzzParameter(0);
}
NamedValue[] headers = request.getHeaders();
if (headers != null) {
for (int i=0; i<headers.length; i++) {
if (headers[i].getName().equals("Cookie"))
continue;
_model.addFuzzHeader(_model.getFuzzHeaderCount(), headers[i]);
}
}
Parameter[] params = Parameter.getParameters(request);
for (int i=0; i<params.length; i++) {
_model.addFuzzParameter(i, params[i], null, 0);
}
}
public Object getScriptableObject() {
return null;
}
public Hook[] getScriptingHooks() {
return new Hook[0];
}
}