SQL truncation injection

Dans cet article nous allons parler de l'injection SQL truncation. Cette attaque repose sur un défaut de validation d'input des utilisateurs qui va nous permettre, via l'exploitation d'une requête INSERT, d'usurper l'identité de l'administrateur d'une application.

Voici la base de données que nous allons utiliser:

CREATE TABLE Users(
    id int auto_increment primary key,
    username VARCHAR(12),
    password VARCHAR(32)
);

Son contenu:

Et voici le code source de l'application vulnérable:

<?php
	try{
		$bdd = new mysqli("localhost", "dbadmin", "dbadmin", "sqltruncation");
	}
	catch (Exception $e){
		die ("Error : ".$e->getMessage());
	}
?>

<!DOCTYPE html>
<html>
	<head>
	</head>

	<body>
		<h1>Welcome to the most secured application ever</h1>		
		
		<p> Sign in! </p>
		<form action="#" method="post">
			<label>Username:</label>
			<input type="text" name="username">
			<br>
			<label>Password:</label>
			<input type="text" name="password">
			<br>
			<input type="submit" value="submit">
		</form>

		<p> Or sign up!</p>
		<form action="#" method="post">
			<label>Username:</label>
			<input type="text" name="user">
			<br>
			<label>Password:</label>
			<input type="text" name="pass">
			<br>
			<input type="submit" value="submit">
		</form>

		<?php
			if (isset($_POST["username"]) and isset($_POST["password"])){
				$req = $bdd->prepare("SELECT * FROM Users WHERE username = 'admin' AND password = ?");
			    $req->bind_param("s", $_POST["password"]);
			    $req->execute();
			    $res = $req->get_result();
			    while($result = $res->fetch_assoc()){
			    	$username = $result["username"];
			    }
				if (isset($username)){
					echo "Bienvenue : ".$username;
				}
				else {
					Header("Location: index.php");
				}
			}
			if (isset($_POST["user"]) and isset($_POST["pass"])){
				$req = $bdd->prepare("INSERT INTO Users (username, password) values(?, ?)");
			    $req->bind_param("ss", $_POST["user"], $_POST["pass"]);
			    $req->execute();
			   echo "Successfuly registered, you can sign in now!";
			}
		?>
	</body>
</html>

Comme nous pouvons le voir l'accès à la partie administration du site est réservé à l'utilisateur dont le nom d'utilisateur est "admin". De plus toutes les requêtes SQL sont préparées ce qui rend l'exploitation d'injections SQL impossibles. En revanche à aucun moment la taille ou le contenu des inputs n'est vérifié.

Dans la déclaration de notre table Users nous avons dit que le champ username de la table Users peut, au maximum, contenir 12 octets:

username VARCHAR(12)

Que se passerait-il si un utilisateur mal intentionné tentait de s'inscrire sur l'application avec le nom d'utilisateur "admin                   whatever" ? Et bien regardons le contenu de la table une fois l'inscription faites:

Un second utilisateur administrateur a été créé et du coup nous pouvons nous connecter à l'application en tant qu'admin:

Alors que s'est-il passé ? Tout d'abord il faut savoir que pour que cette attaque soit réalisable il faut modifier la configuration de MySQL. En effet par défaut MySQL rejettera l'insertion de données ne respectant pas le format d'une table. Cette action de rejet est définie au sein de la variable sql_mode que l'on peut afficher de cette manière:

show variables like 'sql_mode';

Par défaut voici la valeur de cette variable:

Parmi toutes ces options une d'entre elle va nous intéresser plus que les autres: STRICT_TRANS_TABLES. Si cette directive est présente alors l'insertion de données ne respectant pas le format d'une table sera refusée sinon MySQL insérera les données en troquant les informations.

Ce qu'il s'est passé ici c'est que nous avons créé un compte dont le nom d'utilisateur fait plus de 12 caractères du coup MySQL a tronqué ce nom d'utilisateur et supprimer tous les espaces inutiles et c'est ainsi que nous avons pu enregistrer un second compte ayant le nom d'utilisateur "admin".

Comme empêcher ce type d'attaque du coup ? Eh bien il suffira de vérifier la taille du nom d'utilisateur fourni:

<?php
if (sizeof($_POST["username"]) > 12) {
	echo "Did you try hacking me ?";
    die();
}
else {
	echo "Insertion";
}
?>