Injection SQL et procédures stockées

Injection SQL et procédures stockées

Lors d’audits il arrive de trouver des injections SQL un peu différentes du simple SELECT habituel. L’une des injections pouvant perturber un auditeur « débutant » est celle dans une procédure stockée. En effet cette injection change totalement la forme courante de la requête, nous allons donc l’étudier au travers de trois bases de données assez récurrentes, MySQL, Oracle et MsSQL.

Rappel sur les Injections SQL

 

L’injection SQL est une technique consistant à modifier le comportement initial d’une requête afin d’extraire des données supplémentaires ou bien quand cela est possible d’exécuter du code sur le serveur hébergeant la base de données.

Prenons l’exemple simpliste d’une vérification de mot de passe sur une page d’authentification d’un site WEB PHP:

Figure 1 : Code PHP vulnérable aux injections SQL


En envoyant la chaine de caractères suivante dans le paramètre login (méthode GET), « admin’ or ‘1’=’1 » nous nous authentifions en tant qu’administrateur étant donné que nous obtenons la requête SQL suivante :   SELECT id, login, hash from db_user WHERE login=’admin’ or ‘1’=’1  AND hash=’hash…’ LIMIT 1 L’intérêt de l’exploitation de ces vulnérabilités est évident : elles rapportent gros pour une recherche  parfois rapide et souvent simpliste.

Injection SQL et procédures stockées

 

Sous MySQL

Création : Nous allons à l’aide de l’outil en ligne de commande MySQL, créer une procédure stockée, l’appeler et enfin récupérer la valeur voulue. Tout d’abord nous créons la procédure NES, elle consiste uniquement à stocker la valeur 42 dans le premier paramètre de la procédure :  

Figure 2 : Création de la procédure stockée NES

  Nous appelons cette procédure avec le paramètre @test:

Figure 3 : Appel de la procédure stockée NES


Enfin vérifions la valeur stockée dans la variable @test :

Figure 4 : Sélection de la variable de session @test


Nous obtenons donc la valeur 42 précédemment initialisée.

Exploitation: Nous allons donc grâce au script PHP suivant, appeler la procédure stockée (précédemment créé)

Figure 5 : Script PHP faillible aux injections SQL

  L’injection SQL est évidente, il suffit uniquement d’insérer le code voulu à la suite du paramètre name_var (en GET), même pas besoin de quote !

Figure 6 : Exploitation de l’injection SQL en affichant la version de la base de données


Nous obtenons ainsi la version de MySQL utilisée  (5.0.51a) en injectant la chaine de caractères suivante : UNION SELECT @@version

Bon ici ça ne change pas grand choses par rapport aux exploitations classiques mais les cas particuliers arrivent, patience !

 

Sous Oracle

Création :

Tout d’abord, nous créons la procédure simple_execute  grâce à l’interface web isqlplus.

Cette procédure consiste à exécuter via l’appel de  « EXECUTE IMMEDIATE » la requête SQL passée dans la variable « user » de type VARCHAR2 envoyé en premier paramètre de la procédure.

Le résultat de l’exécution de la requête est alors stockée dans la variable de retour « res » :

Figure 7 : Création de la procédure stockée simple_execute


Nous avons maintenant créé la procédure simple_execute.   Dans la Figure 8 nous faisons appel à cette procédure :   set serveroutput on ;  permet d’activer les fonctions du paquetage dbms_output.   Afin d’appeler notre procédure simple_execute, nous avons besoin de deux variables. Nous déclarons donc la variable oparm qui contiendra la valeur de retour et la variable iparm qui contient la chaine de caractères « select 42 from dual ». Puis nous appelons la procédure simple_execute avec les paramètres précédemment déclaré. Finalement nous affichons le résultat de la requête grâce à l’appel de dbms_output.put_line.  

Figure 8 : Appel de la procédure stockée simple_execute et affichage de la réponse de la requête SQL


Tout s’est bien déroulé et nous obtenons le résultat attendu.

 

Exploitation :

Prenons le code PHP fictif suivant :

Figure 9 : Code PHP faillible aux injections SQL


Le code PHP consiste dans un premier temps à récupérer le paramètre « number » de la méthode GET. La variable $number est alors utilisée pour former une chaine de caractères qui sera stokée dans la variable $sql. Cette variable $sql sera alors dans un second temps préparée par la fonction oci_parse.  La chaine de caractères composée par $sql vous l’aurez compris,  est la requête sql qui sera exécutée par la fonction oci_execute.   Pour le besoin de rendu visuel de cet exemple, nous utilisons le binding entre la variable $stmt et la valeur  « res » ( « :res » dans la requête SQL) grâce à la fonction oci_bind_by_name.   L’injection SQL dans le code précédent est simpliste, il suffit uniquement d’insérer le code voulu à la suite du paramètre « number » (en GET),  toujours pas besoin de quote 🙂 (Lors de nos audits cela arrive beaucoup plus fréquemment que l’on y veuille le croire) :   En effet, si nous appelons ce script avec en paramètre la valeur 42 nous obtenons la capture d’écran suivante :

Figure 10 : Affichage du résultat en envoyant la valeur 42 au paramètre number


Nous allons maintenant exploiter cette vulnérabilité en injectant la chaine de caractères suivante dans le paramètre  number  : « 42 || user »

Ceci aura pour but de concaténer la valeur 42 à l’utilisateur Oracle exécutant la requête SQL.

 

Figure 11 : Exploitation


Nous obtenons donc l’utilisateur Oracle par default « SCOTT » concaténé avec la valeur 42. L’exploitation est donc possible.

Sous MS SQL

Création : Une procédure stockée sous MSSQL Server peut être créée en utilisant SQL Server Management Studio. Pour cela il suffit d’exécuter la requête suivante sur la base de données à laquelle on souhaite l’affecter.

Figure 12 : Syntaxe de base de la création d’une procédure stockée MSSQL


La procédure sera ensuite appelée en utilisant la commande EXEC :

 

Figure 13 : Syntaxe de la requête d’exécution de la procédure stockée


Exemple : Moteur de recherche de super héros : Ce petit exemple utilise une procédure stockée afin de rechercher dans la base de données des super héros selon leur nom, leur véritable identité ou bien la ville où ils travaillent.  Cette petite application prend la forme d’un formulaire HTML, celui-ci va appeler un script ASP qui nous permettra d’exécuter la procédure stockée sur le serveur MS SQL. Nous allons donc rechercher un super héros sur la ville de New York :  

Figure 14 : Ce formulaire HTML va appeler un script ASP

 


Figure 15 : L’exécution de la procédure stockée et les résultats retournés


L’exécution de la procédure stockée nous retourne un résultat : Spiderman.  

Figure 16 : La requête SQL exécutée par la procédure stockée appelée ci-dessus


Exploitation :

Nous allons maintenant voir comment exploiter une application vulnérable aux injections SQL et  utilisant une procédure stockée.

Nous commençons par tenter de tronquer la requête en ajoutant des commentaires dans le premier champ :

 

Figure 17 : Requête tronquée après le premier paramètre

 

Figure 18 : Retour d’une erreur 500, la procédure ‘select_superhero’ attend un paramètre supplémentaire : @vrai_nom


Réessayons en fournissant le nom des paramètres avant de tronquer la requête avec des commentaires:

Figure 19 : En fournissant tous ses paramètres à la procédure, il n’y a plus d’erreur


Cette fois, la procédure a reçu tous ses arguments et le serveur répond donc sans erreur. L’injection a donc lieu au moment de l’appel de la procédure stockée : dans la fonction EXEC comme illustré ci-dessous :

Figure 20 : Appel de la fonction EXEC avec l’injection dans un des paramètres de la procédure stockée


NB : Lorsque c’est possible, il est plus facile d’injecter dans le dernier paramètre de la procédure stockée.

NB : Il est également possible d’obtenir l’exécution du code injecté sans passer tous les paramètres de la procédure stockée. Ceci provoquera une erreur du serveur ce qui peut être gênant dans certaines conditions d’exploitation notamment lorsque la valeur de retour est le temps de réponse du serveur. Nous allons maintenant essayer d’injecter une instruction supplémentaire dans notre EXEC. Nous injecterons un waitfor delay, cette fonction bloque l’exécution du lot d’instructions et va nous permettre de d’utiliser le temps de réponse du serveur comme valeur de retour dans le cas d’une exploitation « à l’aveugle ».  

Figure 21 : injection d’un waitfor delay de 5sec : le serveur patiente 5 secondes avant de répondre


Le serveur patiente 5 secondes avant de répondre : le waitfor delay est executé. A partir de là nous pouvons commencer l’exploitation de la vulnérabilité de la même manière qu’avec une injection SQL « classique » sur une requête dynamique.

Exemple : exécution d’un ping sur notre machine depuis le serveur en utilisant la commande « xp_cmdshell » (si la commande est activée pour l’utilisateur SQL en cours):

Figure 22 : Execution d’un ping depuis le serveur en utilisant xp_cmdshell

 

 

Figure 23 : Reception des requêtes de ping sur notre machine


Le ping est reçu par notre machine. De la même manière il est possible d’exécuter n’importe quelle commande DOS depuis l’injection SQL dans une procédure stockée sous MSSQL (avec les droits de l’utilisateur de base de données).

 

A.C. & F.B.

Références :

http://dev.mysql.com/doc/refman/5.0/fr/stored-procedures.html

http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14251/adfns_packages.htm

http://msdn.microsoft.com/fr-fr/library/ms187926.aspx

http://fr.wikipedia.org/wiki/Injection_SQL

https://www.owasp.org/index.php/Guide_to_SQL_Injection

http://home.eng.iastate.edu/~muthu/papers/cnf05.pdf