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 : () / , ; . : #
<> | \ "