Last updated at Wed, 23 Aug 2017 15:38:13 GMT

GestioIP is an open-source IPAM (IP Address Management) solution available on Sourceforge, written in Perl.

There is a vulnerability in the way the ip_checkhost.cgi deals with pinging IPv6 hosts passed to it. If you pass an IPv4 address, the CGI uses a Perl library to perform the ping and return the results to the user.

However, this library doesn't seem to support IPv6 hosts, so the developer uses the ping6 utility to perform the ping of an IPv6 machine. The developer did perform some validation on the values being passed, but it wasn't sufficient and was able to be worked around.

The query string the CGI expects is

 $QUERY_STRING =~ /ip=(.*)&hostname=(.*)&client_id=(.*)&ip_version=(.*)$/; my $ip_ad=$1; my $name=$2 || ""; my $client_id=$3 || ""; my $ip_version=$4 || ""; 

The first check the developer does is testing for any characters that the developer doesn't want in the query string:

 if ( $ENV{'QUERY_STRING'} =~ /[;`'\\<>^%#*]/ ) {         print_html($lang_vars{max_signos_message}, $close);         exit 1; } 

This presented some interesting restrictions on how to exploit the vulnerability.

Once the application has verified that the query string doesn't contain the bad characters (including a space, which isn't included in the previous code), the developer attempts to ensure the IP address is in the correct format.

 if ( $ip_version eq "v4" ) {         if ( $ip_ad !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/ ) {                 print_html("ERROR

$lang_vars{ip_invalid_message}: $ip_ad","");                 exit 1;         } } elsif ( $ip_version eq "v6" ) {         my $ip_ad_expand = ip_expand_address ($ip_ad,6);         if ( $ip_ad_expand !~ /^\w :\w :\w :\w :\w :\w :\w :\w $/ ) {                 print_html("ERROR

$lang_vars{ip_invalid_message} $ip_ad","");         } }

You will notice that there is no 'else' statement, so if the ip_version param doesn't contain either 'ipv4' or 'ipv6', then no validation is done on the $ip_ad variable.

However, even if you were to pass in an ip_version of 'ipv6', the regex for the IPv6 address is not very strict at all.

Since an attacker can bypass any validation of the IP address before being passed to the ping6 command, the only thing left to do is figure out how to get around the first set of character restrictions.

I ended up using a few tricks to get around the fact that I couldn't use a space or a semi-colon. I finally settled on the following IP address in the request which creates a PHP script at the root of the web application.

 2607:f0d0:$(echo${IFS}PD9waHAKCiAgcGhwaW5mbygpOwo/Pgo=|base64${IFS}--decode|tee${IFS}phpinfo.php):0000:0000:0000:0000:0004 

I use ${IFS} instead of a space, which will be substituted by bash by a space. I also use | to go from one command to another and I base64 encode my actual payload to work around bad characters.

Once I figured out how to execute arbitrary commands (and figuring out my payload size couldn't be greater than about 450 characters), I knew how to write my Metasploit module:

And with that, here's the Metasploit module that exercises the vulnerability and can test if you've applied the patch correctly -- the module will be available in the next update, or if you're tracking the Metasploit development branch directly, you can simply use msfupdate to get the goods.