Exploitation de failles de programmation dans les scripts d'authentification Web (Requettes SQL)
Document original par Memonix : http://www.securiteam.com/securitynews/5VP022K56K.html paru le 13/08/2001
Traduit le 13/08/2001 par Cabezon Aurélien http://www.isecurelabs.com/

Introduction
Cet article ne traite pas d'une nouvelle vulnérabilité, mais essait d'expliquer les differents problémes rencontrés dans les
scripts server-side (php, asp etc...) et les moyens de les résoudre.
Le probléme n'est pas lié directement au serveur SQL mais plutot aux requettes SQL qui peuvent étre passées au serveur par le bias des
scripts qui manquent de filtrage au niveau de l'entrée utilisateur.
Sans un tel filtrage, il est possible dans certains cas d'outre passer certains mécanismes d'authentification basé sur une requette SQL pour valider le couple login/password.

Détails
Quand un tel code est utilisé dans un script de login :

    $login = Request.Form("login")
    $password = Request.Form("password")

    SELECT field FROM database WHERE Login=$login AND Password=$password

il est possible de "valider" un login sans savoir le nom d'utilisateur et le mot de passe.

Pour que ce soit possible il est nécéssaire d'avoir certaines condition :

1. La requette SQL doit ressembler à celle-ci:
   
    SELECT filed FROM database WHERE Login=$login AND Password=$Password

où les variables $login et $password résultent des champs du formulaire d'authentification que l'utilisateur rempli pour
s'authentifier (page html, php, asp, cgi...)

2. Le script ne verifie pas la présence pour ces deux variables de mauvais charactéres tels que : ';)"#|

3. Une fois le script validé aucune vérification de mauvais charactéres n'est faite sur ces deux variables.

Si ces 3 conditions sont réunies, il est possible d'outre passer le mécanisme d'authentification.

Forcer la modification de la requette SQL d'authentification
Examinons la requette SQL:

SELECT filed FROM database WHERE Login=$login AND Password=$Password

où les variables $login et $password résultent des champs du formulaire d'authentification que l'utilisateur rempli pour
s'authentifier.

SELECT filed FROM database WHERE Login='' AND Password=''

Quand l'utilisateur essait de s'authentifier en entrant aucun login/password dans le formulaire, les variables $login et
$password prendrons comme valeure "
Ceci limite la vulnérabilité des chaines qui se finissent par ' (Pour que la vulnérabilité fonctionne, il faut une requette
SQL valide, sans erreure).

Une requette SQL normale issue d'une authentification d'un utilisateur normal ressemble à ceci :

    SELECT field FROM database WHERE Login='Jon' AND Password='1234'

ou le login est jon et le mot de passe est 1234

Si vous essayez maintenant en mettant le caractére ' à la place du login, le serveur SQL va retourner " SQL bad syntax
error" puisque que la requette est incompléte

SELECT field FROM database WHERE login="' AND Password="

Nous devons faire attention à fermer correctement la requette SQL (rajouter ' pour faire une requette compléte)
pour créer une requette valide.

Par exmeple:

Si nous remplacons la valeur de $login par 'or"=' et la valeur de $password par 'or"='
nous aurons la requette SQL suivante :

    SELECT field FROM database WHERE Login='' or ''='' AND Password= '' or ''=''

NOTE : 'or"=" retourne toujours VRAI

Que ce passe t'il quand on soument une telle requette SQL qui retourne toujours VRAI ?
La requette SQL va verifier dans la base de donnée si nous avons rentré le bon login et le bon mot de passe.
La requette SQL va retourner VRAI car la vérification du "or" logique va toujours retourner VRAI ! Ce qui fait croire au
script d'authentification que nous avons rentré le bon login et le mot de passe correspondant.De plus, le script
d'authentification, si aucune vérification supplémentaire n'est opérée, nous donneras l'accés sous l'identité du premier
utilisateur inscrit dans la base de donnée, le plus souvent, l'administrateur.

Forcer partiellement la modification de la requette SQL d'authentification
Comme nous l'avous vu ci-dessus, il y a plusieurs moyens de contourner l'authentification si les conditions son réunies.
Il est aussi possible de modifier partiellement la requette SQL ce qui nous permettrait une attaque plus ciblée (login avec un utilisateur connu au lieu de se logger avec le premier utilisateur de la base de données...).

Il est possible de gagner les droits d'accés d'un utilisateur si l'on connais son nom d'utilisateur.

Par exemple:

Si l'on envois toujours par le bias du formulaire la valeur jonn à $login et 'or"=" à $password, alors nous seront capable de nous logger sous l'identité de jonn et nous profiterons de ses droits.

Forcer le serveur SQL à ignorer une partie de la requette SQL
Il est possible de force le serveur à ignorer une partie de la requette SQL en incluant dans la requette SQL envoyée /* et /* ( balises commentaire)

Par exemple:

Si l'on envoit toujours par le bias du formulaire la valeur '/* à $login et */ OR "=' à $password
la requette SQL sera alors :

SELECT field FROM database WHERE Login = ''/*' AND Password = '*/ OR '' = ''

Une fois parsée par le serveur SQL, la requette SQL deviendra :

      SELECT FROM DATABASE WHERE Login = '' OR '' = ''

Cette requette renvoit toujours VRAI !

Voici un autre exmple, dans certains scripts, l'adresse email est utilisée et vérifiée comme Login. Il est possible de créer un login valide, qui passera sans probléme la requette SQL et la vérification du format d'adresse email du script.

Si l'on envoit la valeur '/*mi@mail.com à la variable $login et */ or "=' à la variable $password, le script va parser la variable $login et verifier si c'est bien du format
EMAIL *@*.*, et le serveur SQL va parser la requette en enlevant ce qu'il y a entre les commentaire /* */ nous permettant de passer l'authentification comme nous l'avons vu plus haut.


Solution :

Il existe plusieurs solution pour résoudre ce probléme.
Une solution serait d'utiliser du JavaScript pour valider les charactéres rentrés par l'utilisateur, mais cela peut étre aussi contourné en construisant son propre formulaire d'envois de données au script d'authentification.

Exemple de JavaScript:

<SCRIPT LANGUAGE="JavaScript">
function checkstring(text){
         pat=/^[A-Za-z0-9]{6,10}$/;
         result=text.match(pat);
         return TRUE;
}

function Send(){
         if (checkstring(txtName) && checkstring(txtPassword)){
             login.submit();
         }
}
</SCRIPT>


Voici un autre exemple en PHP:

Avant (aucune vérification pour les charactéres):

  if ($luser!="" && $lpassword!="") {
  $login_rs = @mysql_query("SELECT * FROM cro_user WHERE
  user_login='$luser' AND user_password='$lpassword' AND user_status!='0'
  LIMIT 0,1",$db);
  if (@mysql_num_rows($login_rs)==0) {
         echo "LOGGED ON";
         }
  }


Pour outre passer ce mécanisme vous pouvez entre comme login et mot de passe cette valeur : '|

Voici comment corriger ce probléme:

Aprés (toutes les vérifications sont faites):

  $luser = trim(htmlspecialchars(addslashes($luser)));
  $lpassword = trim(htmlspecialchars(addslashes($lpassword)));
 
  if ($luser!="" && $lpassword!="") {
  $login_rs = @mysql_query("SELECT * FROM cro_user WHERE
  user_login='$luser' AND user_password='$lpassword' AND user_status!='0'
  LIMIT 0,1",$db);
  if (@mysql_num_rows($login_rs)==0) {
         echo "LOGGED ON";
         }
  }


Tous les charactéres spéciaux seront enlevés.
Le but est d'enlever tous les caractéres dangereux tels que ceux ci : () / , ; . : # <> | \ "