lundi 20 avril 2009
Prenons une page vulnérable au SQL Injection, par exemple le code PHP suivant :
$id = $_GET['id']; // mauvais!Comme la valeur $id est concaténée à la requête SQL sans même vérifier le type ni utiliser de placeholders, on peut exploiter cette faille pour extraire de l'information utile pour pratiquer une attaque en règle.
$dbh = new PDO("pgsql:host=localhost;dbname=database", "username", "password");
$sql = 'SELECT * FROM command WHERE command_id = ' . $id;
foreach($dbh->query($sql) as $row) {
echo $row['client_name'] . '\n';
}
Pour valider que le site repose sur la base de données Postgre, il suffit d'ajouter à la suite de la query string une condition en utilisant l'opérateur :: (cast) qui convertit la valeur en nombre entier (par exemple) et qui évalue l'équivalence pour donner une condition vraie. Ici, on veut qu'elle soit positive pour que l'enregistrement soit quand même retourné et que l'affichage se fasse normalement.
page.php?id=100 AND 1::int = 1--
Si aucune erreur ne se produit, c'est que PostgreSQL est utilisé puisque cette syntaxe lui est propre. Ensuite, on peut tentez une autre requête pour connaître la version installée. L'idée est de créer un enregistrement supplémentaire qui s'affichera dans la page, suite à une boucle par exemple :
page.php?id=100 UNION ALL SELECT null, version();--
Le truc est de deviner le nombre de champs retournés par la requête, de substituer une valeur nulle pour chacun et de s'arranger pour que la valeur à extraire se retrouve au sein d'un champ varchar. Une fois le bon pattern découvert pour le recordset à retourner, on peut facilement extraire :
- le nom de la base de données : current_database()
- le nom d'utilisateur : utiliser la constante current_user ou la fonction getpgusername()
Rappelez-vous de toujours vérifier les paramètres reçus. Dans mon exemple PHP, une simple vérification de la valeur de $_GET['id'] avec is_numeric() aurait été suffisant pour contrer ce type d'injection. Une autre protection possible serait d'attribuer des droits restreints à l'utilisateur qui se connecte par le web. On peut alors limiter les dégâts...