/***********************************************************************
*
* $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
*
*/
/*
* SessionidAnalysis.java
*
* Created on 16 November 2003, 05:19
*/
package org.owasp.webscarab.plugin.sessionid;
import java.io.BufferedWriter;
import java.io.FileWriter;
import org.owasp.webscarab.httpclient.ConversationHandler;
import org.owasp.webscarab.httpclient.FetcherQueue;
import org.owasp.webscarab.httpclient.HTTPClientFactory;
import org.owasp.webscarab.model.StoreException;
import org.owasp.webscarab.model.Cookie;
import org.owasp.webscarab.model.HttpUrl;
import org.owasp.webscarab.model.NamedValue;
import org.owasp.webscarab.model.ConversationID;
import org.owasp.webscarab.model.Request;
import org.owasp.webscarab.model.Response;
import org.owasp.webscarab.plugin.Framework;
import org.owasp.webscarab.plugin.Plugin;
import org.owasp.webscarab.plugin.Hook;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Map;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
/**
*
* @author rdawes
*/
public class SessionIDAnalysis implements Plugin, ConversationHandler {
private SessionIDModel _model;
private FetcherQueue _fetcherQueue;
private int _threads = 4;
private String _name = null;
private String _regex = null;
private int _count = 0;
private Request _request = null;
private Response _response = null;
private Thread _runThread = null;
private Logger _logger = Logger.getLogger(getClass().getName());
/** Creates a new instance of SessionidAnalysis */
public SessionIDAnalysis(Framework framework) {
_model = new SessionIDModel(framework.getModel());
_fetcherQueue = new FetcherQueue("SessionID", this, _threads, 100);
}
public SessionIDModel getModel() {
return _model;
}
public void setSession(String type, Object store, String session) throws StoreException {
if (type.equals("FileSystem") && (store instanceof File)) {
_model.setStore(new FileSystemStore((File) store));
} else {
throw new StoreException("Store type '" + type + "' is not supported in " + getClass().getName());
}
}
/** The plugin name
* @return The name of the plugin
*
*/
public String getPluginName() {
return new String("Session ID Analysis");
}
public void run() {
_model.setStatus("Started");
_model.setRunning(true);
_runThread = Thread.currentThread();
_model.setStopping(false);
while (! _model.isStopping()) {
while (_request != null && _count > 0 && _fetcherQueue.getRequestsQueued() < _threads) {
_fetcherQueue.submit(_request);
}
try {
Thread.sleep(100);
} catch (InterruptedException ie) {}
}
_request = null;
_fetcherQueue.clearRequestQueue();
_model.setRunning(false);
_model.setStatus("Stopped");
}
public void requestError(Request request, IOException ioe) {
_logger.info("Requested " + request.getURL() + " got IOException " + ioe.getMessage());
}
public void responseReceived(Response response) {
if (_count == 0) return;
_count--;
Map<String, SessionID> ids = getIDsFromResponse(response, _name, _regex);
Iterator<String> it = ids.keySet().iterator();
while (it.hasNext()) {
String key = it.next();
SessionID id = ids.get(key);
_model.addSessionID(key, id);
}
}
public Map<String, SessionID> getIDsFromResponse(Response response, String name, String regex) {
Map<String, SessionID> ids = new TreeMap<String, SessionID>();
Request request = response.getRequest();
if (request == null) {
System.out.println("Request was null?");
return ids;
}
HttpUrl url = request.getURL();
Date date = new Date();
NamedValue[] headers = response.getHeaders();
if (name != null && !name.equals("") && regex != null) {
String location = response.getHeader("Location");
if (location != null) {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(location);
if (matcher.matches() && matcher.groupCount() > 0) {
for (int j=1; j<=matcher.groupCount(); j++) {
SessionID id = new SessionID(date, matcher.group(j));
ids.put(name + " " + j, id);
}
}
}
String type = response.getHeader("Content-Type");
if (type != null && type.startsWith("text/")) {
String charset = "UTF-8";
String body = null;
try {
body = new String(response.getContent(), charset);
} catch (UnsupportedEncodingException uee) {
body = new String(response.getContent());
}
Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE | Pattern.DOTALL);
Matcher matcher = pattern.matcher(body);
if (matcher.matches() && matcher.groupCount() > 0) {
for (int j=1; j<=matcher.groupCount(); j++) {
SessionID id = new SessionID(date, matcher.group(j));
ids.put(name + " " + j, id);
}
}
}
} else {
Pattern pattern = Pattern.compile("(.*)");
if (regex != null && !regex.equals("")) pattern = Pattern.compile(regex);
for (int i=0; i<headers.length; i++) {
if (headers[i].getName().equalsIgnoreCase("Set-Cookie") || headers[i].getName().equalsIgnoreCase("Set-Cookie2")) {
Cookie cookie = new Cookie(date, url, headers[i].getValue());
Matcher matcher = pattern.matcher(cookie.getValue());
name = cookie.getKey();
if (matcher.matches()) {
SessionID id = new SessionID(date, matcher.group(0));
ids.put(name, id);
if (matcher.groupCount() > 0) {
for (int j=1; j<=matcher.groupCount(); j++) {
if (!matcher.group(j).equals(matcher.group(0))) {
id = new SessionID(date, matcher.group(j));
ids.put(name + " " + j, id);
}
}
}
}
}
}
}
return ids;
}
public void fetch(Request request, String name, String regex, int count) {
// throws a Runtime exception if this failes
Pattern.compile(regex);
_request = request;
_name = name;
_regex = regex;
_count = count;
}
public void setRequest(Request request) {
_request = request;
}
public void fetchResponse() throws IOException {
_response = HTTPClientFactory.getInstance().fetchResponse(_request);
}
public Response getResponse() {
return _response;
}
/** This function provides for setting a different calculator
* i.e. one that calculates the sessionid based on different
* criteria to the DefaultCalculator, such as character ordering, etc
*
* No interface component uses it as yet, though
*/
public void setCalculator(String key, Calculator calc) {
_model.setCalculator(key, calc);
}
public boolean stop() {
_model.setStopping(true);
try {
_runThread.join(5000);
} catch (InterruptedException ie) {
_logger.warning("Interrupted!");
}
return !_model.isRunning();
}
public void flush() throws StoreException {
_model.flush();
}
public boolean isBusy() {
return _count > 0;
}
public String getStatus() {
return _model.getStatus();
}
public boolean isModified() {
return _model.isModified();
}
public boolean isRunning() {
return _model.isRunning();
}
public void analyse(ConversationID id, Request request, Response response, String origin) {
HttpUrl url = request.getURL();
String cookie = request.getHeader("Cookie");
if (cookie != null) _model.addRequestCookie(id, cookie);
String[] setCookie = response.getHeaders("Set-Cookie");
if (setCookie != null) {
for (int i=0; i<setCookie.length; i++) {
Cookie c = new Cookie(new Date(), setCookie[i]);
_model.addResponseCookie(id, url, c);
}
}
}
public Object getScriptableObject() {
return null;
}
public Hook[] getScriptingHooks() {
return new Hook[0];
}
public void clearSessionIDs(String key) {
_model.clearSessionIDs(key);
}
public void exportIDSToCSV(String key, File file) throws IOException {
int count = _model.getSessionIDCount(key);
if (count == 0) return;
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
StringBuffer buff = new StringBuffer();
for (int i=0; i<count; i++) {
SessionID id = _model.getSessionIDAt(key, i);
buff.append(id.getDate().getTime());
buff.append(",").append(_model.getSessionIDValue(key, id));
buff.append(",").append(id.getValue()).append("\n");
bw.write(buff.toString().toCharArray());
buff.delete(0, buff.length());
}
bw.close();
}
}