La faille MySQL Column truncation

mysql column truncation

Hello les amis !

Aujourd’hui, je vous parlerai d’un nouveau type de faille, il s’agit de MySQL column truncation!

Pendant que les failles du types SQL Injections restent parmi les vecteurs d’attaque les plus discutés sur le Web, il existe un autre genre qui a été discuté pour la première fois par Stefan Esser, ce dernier a illustré son exploitation dans un bug reporté sur bugtraq affectant wordpress!

Comment ça marche ?

Par défaut, la comparaison de deux chaine de caractère dans MySQL ne se fait pas en mode binaire, ceci se fait en “relaxed mode”, ce mode compare deux chaines on ignorant les espaces et les caractères nuls de la fin de la chaine, du coup, pour mysql, la chaine de caractères ‘admin’ est égale à ‘admin ‘! .

De ce fait mysql refusera d’enregistrer un nouvel utilisateur qui essaie de s’incrire avec ‘admin ‘ (admin+espace) ce qui est tout à fait logique!

Ou est le problème?

Pour illustrer le problème on immagine l’application suivante:

  • Un forum de discution ou un blog ou tous le monde peut s’enregistrer
  • Le champs username de la table utilisateurs est limité à 16 caractères
  • L’administrateur du forum est ‘admin’
  • MySQL est utilisé dans le mode par défaut
  • Pas de vérification sur la longueur du nom d’utilisateur

Qu’est ce qui se passe si on essaie de créer un compte avec comme nom d’utilisateur ‘admin xD’ (admin+11 espaces+xD) et un mot de passe ‘p455wd’:

MySQL va cherche si il existe un utilisateur avec comme username ‘admin xD’, et comme aucun utilisateur ne l’as pris l’application va l’accepter et continuera pour inserer le nouvel utilisateur dans la base.

La chaine du nom d’utilisateur fait 18 caractères (admin+11 espaces+xD), mySQL va automatiquement tronquer les deux dernier caractères (xD) car le champ username est limité à 16 caractères, ce qui donnera ‘admin ‘ (admin+11 espaces), ce dernier sera accepté par l’application et inséré dans la table d’utilisateurs, on aura donc deux utilisateur avec le même login ‘admin’ (rappellez vous que la comparaison en mysql dans le mode par défaut ne prends pas en compte les espaces!).

Dans ce cas, un problème de sécurité potentiel peut affecter l’application, en dépendance de la façon dont elle traite le champ username!

Exemple d’une application vulnérable:
Connexion :
[code lang=’php’]
if(!empty($_POST[‘username’]) && !empty($_POST[‘password’]){
$username = $_POST[‘username’];
$password= $_POST[‘password’];
if(login($username,$password)){
$userdata = getUserInfoByLogin($username);
}
}
[/code]

la fonction getUserInfoByLogin()

[code lang=’php’]

function getUserInfoByLogin($username){

$query = mysql_query(“SELECT * FROM utilisateurs WHERE username = ‘$username”);

$userinfo= mysql_fetch_array($query);

return $userinfo ;

}

[/code]

Espace membre

[code lang=’php’]

if($userdata[‘username’] == ‘admin’){

//espace privé : console d’administration

}

else{

// espace membre!

}[/code]

Resultat:

Puisque l’attaquant a un compte avec le login ‘admin ‘, il pourra se connecter à ce dernier avec le passe ‘p455wd’ ! et puisque l’enregistrement du vrai administrateur est le premier en base, celuila sera retourné ! et l’attaquant aura accès à l’espace administrateur du forum!

Ceci est juste une illustration, la faille peut se présenter différemment sur une application web,je cite là wordpress qui était le premier victime de ce type de vulnérabilités!

Comment sécuriser mon application?

Toujours vérifier la longueur des champs sensibles (comme username dans notre exemple), et interrompre l’exécution de votre application si cella dépasse la longueur maximale configurée dans votre table mysql!

9 thoughts on “La faille MySQL Column truncation

  1. OMG impressionnant ! j’apprends tout les jours quelques chose de nouveau sur ton blog, très informatif attend que je fais quelques tests :O

  2. La solution serait donc un
    if(strlen($_POST[‘username’]) > 100){
    echo”Pseudo trop long”;
    }

    ou mieux encore limiter les caractères à 100 et interdire les espaces et les caractères spéciaux genre :

    preg_match(“/^[a-z0-9_\-]{5,100}$/”, $_POST[‘username’]);

  3. comme d’habitude un bon sujet !

    mais

    pour un varchar par exemple ça me donne cette erreur
    #1406 – Data too long for column ‘df’ at row 1

    pour un TEXT ca l’enregistre !

  4. @Mehdi, c’est quoi la version mysql que tu utilise? avait elle la configuration par defaut?

    fait ce petit test et dis moi ce que ça donne :
    Tu crée la table utilisateurs :

    CREATE TABLE `utilisateurs` (
    `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
    `username` VARCHAR( 12 ) NOT NULL ,
    `password` VARCHAR( 32 ) NOT NULL
    ) ENGINE = MYISAM

    Puis tu insère un utilisateur dont le nom dépasse 12 caractères

    INSERT INTO `test`.`utilisateurs` (
    `id` ,
    `username` ,
    `password`
    )
    VALUES (
    NULL , ‘abcdefghijklmno’, MD5( ‘test’ )
    );

    abcdefghijklmno ça fait 15 caractères, la requête sql aboutit mais un warning s’affiche! (cf http://www.mcherifi.org/data/mysqltruncation/warningtruncated.png )

    si on affiche la les lignes de la table user on trouvé bien notre enregistrement mais tronqué à 12 caractères (cf http://www.mcherifi.org/data/mysqltruncation/truncatedcolumn.png)

    par-contre pour un champs de type TEXT il n’y aura pas de warning, il sera correctement enregistré!

  5. Version du client MySQL: 5.0.51a

    ça donne :

    #1406 – Data too long for column ‘username’ at row 1

    et rien n’est enregistré …

Leave a Reply

Your email address will not be published. Required fields are marked *