Injection SQL : contourner les filtres et les WAF
Un filtre d’entrée ou un WAF qui échappe les quotes et bloque quelques mots-clés donne une impression de protection. Plusieurs techniques la traversent, en faisant arriver à la base un payload que le filtre n’a pas su reconnaître. Elles supposent une injection déjà identifiée, dont la méthodologie est décrite dans la fiche « Injection SQL UNION ».
Bypass de charset multi-octets
Section intitulée « Bypass de charset multi-octets »Beaucoup d’applications échappent les quotes en préfixant chaque ' d’un antislash (addslashes, \'). Cette protection suppose un encodage sur un octet. Si la base interprète le flux dans un charset multi-octets comme GBK, la combinaison peut se retourner contre le filtre.
Le principe : l’octet 0xbf suivi d’un 0x27 (la quote) forme, une fois l’antislash inséré, une séquence 0xbf 0x5c qui est un caractère valide à deux octets en GBK. La quote qui suit se retrouve alors libre, non échappée.
# La quote 0x27 précédée de 0xbf : après échappement, la quote reste activedatas = {"login": chr(0xbf) + chr(0x27) + "OR 1=1 #", "password": "test"}À l’écran, la séquence se présente comme un caractère multi-octets suivi d’une quote fonctionnelle :
運' OR 1=1 #Le filtre a bien vu une quote et l’a échappée, mais la base la lit comme un caractère valide accolé à une quote libre. L’injection passe.
Encodage hexadécimal des chaînes
Section intitulée « Encodage hexadécimal des chaînes »Quand le payload doit contenir une chaîne littérale (un nom de table, une valeur de comparaison), les quotes qui l’entourent trahissent l’injection. En MySQL et SQLite, une valeur hexadécimale 0x... est automatiquement retransformée en texte par la base : on remplace donc toute chaîne quotée par son équivalent hexadécimal, sans une seule quote.
-- Au lieu de table_name='users'... WHERE table_name = 0x7573657273 -- 0x7573657273 = "users"
-- Séparateur ':' sans quote, pour concaténer login et mot de passeGROUP_CONCAT(login, 0x3a, password) -- 0x3a = ":"La conversion d’une chaîne en hexadécimal se fait avec n’importe quel utilitaire :
echo -n "users" | xxd -p # -> 7573657273Cette technique est centrale dès qu’un filtre bloque le caractère quote, et elle est le pivot de la routed injection traitée dans sa propre fiche.
Substituer les mots-clés bloqués
Section intitulée « Substituer les mots-clés bloqués »Un WAF liste souvent des mots-clés à refuser. Quand l’un est bloqué, un équivalent fonctionnel passe parfois au travers.
ORDER BY, fréquemment surveillé pour le comptage de colonnes, a un substitut pour sonder la structure : GROUP BY.
admin' GROUP BY 1 -- -admin' GROUP BY 2 -- -Comme ORDER BY, GROUP BY échoue dès que l’index dépasse le nombre de colonnes, ce qui donne la même information tout en évitant le mot-clé filtré.
Varier les commentaires de fin
Section intitulée « Varier les commentaires de fin »Le motif de commentaire qui neutralise la fin de requête a plusieurs formes, utiles quand l’une est filtrée ou mal interprétée :
' OR 1=1 -- -' OR 1=1 #' OR 1=1 --Le -- - ajoute un caractère après le double tiret pour garantir l’espace requis par certains moteurs, là où un simple -- suivi d’un espace peut être normalisé.
Remédiation
Section intitulée « Remédiation »Aucune de ces techniques ne fonctionne face à une requête préparée : les données ne sont jamais interprétées comme du code, quel que soit leur encodage. Un WAF ou un filtre d’entrée ne sont qu’une couche de défense en profondeur, jamais la protection principale. Côté charset, fixer un encodage cohérent (utf8mb4) et utiliser l’échappement propre au driver ferme le vecteur multi-octets.
À retenir
Section intitulée « À retenir »Échapper les quotes ne suffit pas : un charset multi-octets peut libérer la quote échappée, l’hexadécimal reconstitue n’importe quelle chaîne sans quote, et un mot-clé bloqué a souvent un équivalent (GROUP BY pour ORDER BY). Ces contournements visent tous des protections qui inspectent la forme de l’entrée. Seule la requête préparée, qui sépare le code des données, les rend inopérants.