Vous devez examiner tout le code qui appelle EXECUTE, EXEC ou sp_executesql. Vous pouvez utiliser des requêtes similaires à l'exemple suivant pour faciliter l'identification des procédures contenant ces instructions. Cette requête recherche les 1, 2, 3 ou 4 espaces après les mots EXECUTE ou EXEC.
SELECT object_Name(id) FROM syscomments
WHERE UPPER(text) LIKE '%EXECUTE
(%'
OR UPPER(text) LIKE '%EXECUTE (%'
OR UPPER(text) LIKE '%EXECUTE (%'
OR UPPER(text) LIKE '%EXECUTE (%'
OR UPPER(text) LIKE '%EXEC (%'
OR UPPER(text) LIKE '%EXEC (%'
OR UPPER(text) LIKE '%EXEC (%'
OR UPPER(text) LIKE '%EXEC (%'
OR UPPER(text) LIKE '%SP_EXECUTESQL%'
Enveloppement de paramètres avec QUOTENAME() et REPLACE()
Dans chaque procédure stockée sélectionnée, vérifiez que toutes les variables utilisées dans du Transact-SQL dynamique sont traitées correctement. Les données issues des paramètres d'entrée de la procédure stockée ou lues à partir d'une table doivent être enveloppées dans QUOTENAME() ou REPLACE(). Gardez à l'esprit que la valeur de @variable transmise à QUOTENAME() est de type sysname et sa longueur maximale de 128 caractères.
|
@variable
|
Wrapper recommandé
|
|---|
|
Nom d'un sécurisable
|
QUOTENAME(@variable)
|
|
Chaîne de ≤ 128 caractères
|
QUOTENAME(@variable, '''')
|
|
Chaîne de > 128 caractères
|
REPLACE(@variable,'''', '''''')
|
Lorsque vous utilisez cette technique, une instruction SET peut être révisée comme suit :
--Before:
SET @temp = N'select * from authors where au_lname='''
+ @au_lname + N''''
--After:
SET @temp = N'select * from authors where au_lname='''
+ REPLACE(@au_lname,'''','''''') + N''''
Injection activée par la troncature des données
Tout code Transact-SQL dynamique affecté à une variable sera tronqué s'il est d'une taille supérieure à la mémoire tampon allouée à cette variable. Un attaquant capable de forcer la troncature d'une instruction en transmettant des chaînes inhabituellement longues à une procédure stockée peut manipuler le résultat. Par exemple, la procédure stockée créée par le script suivant court un risque d'injection permis par la troncature.
CREATE PROCEDURE sp_MySetPassword
@loginname sysname,
@old sysname,
@new sysname
AS
-- Declare variable.
-- Note that the buffer here is only 200 characters long.
DECLARE @command varchar(200)
-- Construct the dynamic Transact-SQL.
-- In the following statement, we need a total of 154 characters
-- to set the password of 'sa'.
-- 26 for UPDATE statement, 16 for WHERE clause, 4 for 'sa', and 2 for
-- quotation marks surrounded by QUOTENAME(@loginname):
-- 200 – 26 – 16 – 4 – 2 = 154.
-- But because @new is declared as a sysname, this variable can only hold
-- 128 characters.
-- We can overcome this by passing some single quotation marks in @new.
SET @command= 'update Users set password=' + QUOTENAME(@new, '''') + ' where username=' + QUOTENAME(@loginname, '''') + ' AND password = ' + QUOTENAME(@old, '''')
-- Execute the command.
EXEC (@command)
GO
En transmettant 154 caractères dans une mémoire tampon de 128 caractères, un attaquant peut définir un nouveau mot de passe pour sa sans connaître l'ancien.
EXEC sp_MySetPassword 'sa', 'dummy', '123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012'''''''''''''''''''''''''''''''''''''''''''''''''''
Pour cette raison, vous devez utiliser une mémoire tampon de grande taille pour une variable de commande ou exécuter directement le Transact-SQL dynamique à l'intérieur de l'instruction EXECUTE.
Troncature lors de l'utilisation de QUOTENAME(@variable, '''') et REPLACE()
Les chaînes renvoyées par QUOTENAME() et REPLACE() seront tronquées en mode silencieux si elles dépassent l'espace alloué. La procédure stockée créée dans l'exemple suivant illustre les conséquences possibles.
CREATE PROCEDURE sp_MySetPassword
@loginname sysname,
@old sysname,
@new sysname
AS
-- Declare variables.
DECLARE @login sysname
DECLARE @newpassword sysname
DECLARE @oldpassword sysname
DECLARE @command varchar(2000)
-- In the following statements, the data stored in temp variables
-- will be truncated because the buffer size of @login, @oldpassword,
-- and @newpassword is only 128 characters, but QUOTENAME() can return
-- up to 258 characters.
SET @login = QUOTENAME(@loginname, '''')
SET @oldpassword = QUOTENAME(@old, '''')
SET @newpassword = QUOTENAME(@new, '''')
-- Construct the dynamic Transact-SQL.
-- If @new contains 128 characters, then @newpassword will be '123... n
-- where n is the 127th character.
-- Because the string returned by QUOTENAME() will be truncated,
-- it can be made to look like the following statement:
-- UPDATE Users SET password ='1234. . .[127] WHERE username=' -- other stuff here
SET @command = 'UPDATE Users set password = ' + @newpassword
+ ' where username =' + @login + ' AND password = ' + @oldpassword;
-- Execute the command.
EXEC (@command)
GO
Par conséquent, l'instruction suivante va définir les mots de passe de tous les utilisateurs à la valeur qui a été transmise dans le code précédent.
EXEC sp_MyProc '--', 'dummy', '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678'
Vous pouvez forcer la troncature de chaîne en dépassant l'espace de mémoire tampon alloué lors de l'utilisation de REPLACE(). La procédure stockée créée dans l'exemple suivant illustre les conséquences possibles.
CREATE PROCEDURE sp_MySetPassword
@loginname sysname,
@old sysname,
@new sysname
AS
-- Declare variables.
DECLARE @login sysname
DECLARE @newpassword sysname
DECLARE @oldpassword sysname
DECLARE @command varchar(2000)
-- In the following statements, data will be truncated because
-- the buffers allocated for @login, @oldpassword and @newpassword
-- can hold only 128 characters, but QUOTENAME() can return
-- up to 258 characters.
SET @login = REPLACE(@loginname, '''', '''''')
SET @oldpassword = REPLACE(@old, '''', '''''')
SET @newpassword = REPLACE(@new, '''', '''''')
-- Construct the dynamic Transact-SQL.
-- If @new contains 128 characters, @newpassword will be '123...n
-- where n is the 127th character.
-- Because the string returned by QUOTENAME() will be truncated, it
-- can be made to look like the following statement:
-- UPDATE Users SET password='1234…[127] WHERE username=' -- other stuff here
SET @command= 'update Users set password = ''' + @newpassword + ''' where username='''
+ @login + ''' AND password = ''' + @oldpassword + '''';
-- Execute the command.
EXEC (@command)
GO
Comme avec QUOTENAME(), la troncature de chaîne par REPLACE() peut être évitée en déclarant des variables temporaires de taille suffisante pour tous les cas. Dans la mesure du possible, vous devez appeler QUOTENAME() ou REPLACE() directement à l'intérieur du Transact-SQL dynamique. Sinon, vous pouvez calculer la taille requise de la mémoire tampon de la manière suivante. Pour @outbuffer = QUOTENAME(@input), la taille de @outbuffer doit être 2*(len(@input)+1). . Lorsque vous utilisez REPLACE() et des apostrophes doubles, comme dans l'exemple précédent, une mémoire tampon de 2*len(@input) est suffisante.
Le calcul suivant couvre tous les cas :
While len(@find_string) > 0, required buffer size =
round(len(@input)/len(@find_string),0) * len(@new_string)
+ (len(@input) % len(@find_string))
Troncature lors de l'utilisation de QUOTENAME(@variable, ']')
La troncature peut se produire quand le nom d'un sécurisable SQL Server est transmis à des instructions utilisant la forme QUOTENAME(@variable, ']'). L'exemple suivant illustre cela.
CREATE PROCEDURE sp_MyProc
@schemaname sysname,
@tablename sysname,
AS
-- Declare a variable as sysname. The variable will be 128 characters.
-- But @objectname actually must allow for 2*258+1 characters.
DECLARE @objectname sysname
SET @objectname = QUOTENAME(@schemaname)+'.'+ QUOTENAME(@tablename)
-- Do some operations.
GO
Lorsque vous concaténez des valeurs de type sysname, vous devez utiliser des variables temporaires de taille suffisante pour contenir le maximum de 128 caractères par valeur. Dans la mesure du possible, appelez QUOTENAME() directement à l'intérieur du Transact-SQL dynamique. Sinon, vous pouvez calculer la taille requise de la mémoire tampon comme expliqué dans la section précédente.