"Bonjour, Checkmarx!" Comment écrire une demande pour Checkmarx SAST et trouver des vulnérabilités intéressantes



Bonjour, Habr!

Dans l'article, je veux parler de notre expérience dans la création de mes requêtes dans Checkmarx SAST.

Lorsque vous vous familiarisez avec cet analyseur pour la première fois, vous pouvez avoir l'impression qu'en plus de rechercher des algorithmes de chiffrement / hachage faibles et un tas de faux positifs, il ne renvoie rien d'autre. Mais lorsqu'il est correctement configuré, c'est un outil super puissant qui peut rechercher des bugs graves.

Nous allons comprendre les subtilités du langage de requête Checkmarx SAST et écrire 2 requêtes pour rechercher des injections SQL et des références d'objet direct non sécurisées.


Entrée


Après une longue recherche de guides ou d'articles sur Checkmarx, il est devenu clair pour moi qu'en dehors de la documentation officielle, il n'y avait pas assez d'informations utiles. Et la documentation officielle ne dit pas que tout devient très clair et compréhensible. Par exemple, je n'ai trouvé aucune meilleure pratique, comment organiser correctement les requêtes de remplacement, comment écrire une requête «pour les nuls», etc. Oui, il existe une documentation sur les fonctions du langage de requête CMx, mais voici comment combiner ces fonctions en une seule requête, la documentation n'est pas écrite.

Peut-être que le manque d'articles et de guides de la communauté Checkmarx est associé au coût élevé de l'outil et, par conséquent, à un public restreint. Ou peut-être que peu de gens s'embarrassent du réglage fin et utilisent la solution telle quelle, prête à l'emploi.

D'après mon expérience, je constate davantage que SAST est davantage utilisé pour se conformer aux formalités liées aux diverses exigences des clients que pour rechercher de vrais bugs. Avec cette approche, nous avons donc, au mieux, un nombre relativement faible de «vulnérabilités» que l'on appelle presque automatiquement «non exploitables» (car elles sont dans 99,9% des cas).

Il convient de noter que Checkmarx essaie lui-même de mettre à jour ses requêtes afin de donner le meilleur résultat prêt à l'emploi. Mais les requêtes CMx Query Language sont adaptées au «cas général». La recherche initiale de jetons est basée sur le nom. Par exemple, CMx SAST suppose que toutes les requêtes vers la base de données ressembleront à ceci: * createQuery * ou * createSQLQuery *. Mais si le développement interne est utilisé pour travailler avec la base de données et que la méthode d'interrogation de la base de données est appelée différemment, par exemple * driveMyQuery *, toutes les méthodes SQL seront ignorées. Par exemple, notre client utilise ORM personnalisé pour SQL DB. Dans ce cas, les requêtes CMx prêtes à l'emploi ont ignoré toutes les injections SQL.

Abréviations et définitions


CMx - Checkmarx SAST.
CMxQL - Langage de requête Checkmarx SAST
Token - une chaîne avec une certaine valeur est le résultat du travail de l'analyseur lexical (qui est aussi appelé tokenisation)

Application de test


Pour écrire un article, j'ai esquissé du code Java, une petite application de test. Ce code est une copie approximative d'une petite partie du système réel. Bien qu'en général le code de l'application de test ne soit pas très différent de tout autre code backend HTTP. Les sections clés du code de l'application de test seront visibles sur les captures d'écran.

L'application de test a la structure suivante


Classe WebRouter pour le traitement des requêtes HTTP entrantes; 4 méthodes de traitement des URL à l'intérieur:
  • / getTransaction - accepte l' ID de transaction à l'entrée et renvoie les informations dessus, id le prend comme chaîne et le transmet à getTransactionInfo (transactionId) => getTransactionInfo (transactoinId) - fait que transactionId concatène à la requête SQL (c'est-à-dire que l'injection SQL est obtenue);
  • / getSecureTransaction - accepte l' ID de transaction en entrée et renvoie les informations dessus, id le prend comme chaîne et le transmet getTransactionInfoSecured () => getTransactionInfoSecured (transactoinId) - convertit d' abord la chaîne transactionId en type Long, puis la concatène à la requête SQL (dans ce cas où l'injection n'est pas exploitée);
  • / getSettings - accepte userId et mailboxId comme entrée - et émet les paramètres de boîte aux lettres. Ne vérifie pas que l' ID de boîte aux lettres appartient à l'utilisateur;
  • / getSecureSettings - accepte également userId et mailboxId à l'entrée et affiche les paramètres de boîte aux lettres. MAIS vérifie que l' ID de boîte aux lettres appartient à l'utilisateur.


CMx: informations générales et définitions de base


Avant de commencer à développer des requêtes


Le développement des requêtes est effectué dans un programme distinct CxAuditor. Dans CxAuditor, vous devez analyser tout le code (créer un projet local), pour lequel nous écrirons des requêtes. Après cela, vous pouvez écrire et exécuter de nouvelles requêtes. Avec une grande base de code, l'analyse principale peut prendre des heures et des gigaoctets de mémoire. Après cela, chaque demande ne sera pas exécutée assez rapidement. Ceci est totalement inadapté au développement.

Par conséquent, vous pouvez prendre un petit ensemble de fichiers du projet, idéalement avec un bogue trouvé dans le code avant le type sous lequel nous écrivons une demande (ou y mettre le bogue avec vos mains) et analyser uniquement cet ensemble de fichiers. Il n'est pas nécessaire de respecter la structure de fichiers du projet. Autrement dit, si vous avez le package Java A et B, et que les classes du package B utilisent les classes et les méthodes du package A, vous pouvez mettre tout cela dans un seul répertoire, et CMx comprendra toujours les relations et construira correctement les chaînes d'appels entre les fichiers (enfin, ou presque toujours correct, bien que les erreurs soient à peine liées à la structure des fichiers du projet).

Définitions de base


Cxlist


Le type de données principal dans CMx. Le résultat de presque toutes les fonctions CMxQL sera CxList . C'est beaucoup d'éléments avec certaines propriétés. Les propriétés les plus utiles pour le développement seront examinées ci-dessous.

résultat


CMxQL a un résultat variable intégré. L'ensemble qui contient la variable de résultat , après l'exécution de la requête entière, sera affiché comme résultat.

Autrement dit, l'opération finale de toute requête doit être la chaîne result = WHATEVER , par exemple:
result = All.FindByName("anyname"); 

élément de flux et de code


La plupart des fonctions CMxQL par type de valeurs retournées sont divisées en 2, celles qui renvoient des "éléments de code" et celles qui renvoient Flow. Dans les deux cas, le résultat est une CxList . Mais son contenu sera légèrement différent pour les éléments Flow et code.
  • Élément de code - jeton - par exemple, une variable, un appel de méthode, une affectation, etc.
  • Flow - la relation entre les jetons donnés.


Tous et «sous» tous


Chaque fonction CMxQL peut être exécutée soit sur l'ensemble All (il contient tous les jetons de tout le code analysé, nous avons déjà vu un exemple avec résultat ) ou sur l'ensemble CxList , qui à son tour a été obtenu à la suite de certaines opérations de la requête, par exemple, la requête:
 CxList newList = CxList.New(); 

va créer un ensemble vide, que nous pouvons ensuite remplir avec des éléments en utilisant la méthode Add () , puis rechercher déjà par les éléments du nouvel ensemble:
 CxList newFind = newList.FindByName("narrowedScope"); 

Propriétés des objets trouvés


Chaque élément de l'ensemble CxList possède plusieurs propriétés. Lors de l'analyse des résultats de l'écriture de requêtes, les plus utiles sont:

  • SourceFile - le nom du fichier qui contient cet élément;
  • Ligne source - numéro de ligne avec jeton;
  • Nom de la source - le nom du jeton. Équivalent au jeton, c'est-à-dire si la variable est appelée var1, alors Nom de la source = var1;
  • Type de source - le type de jeton. Par exemple, s'il s'agit d'une chaîne, ce sera StringLiteral, si la méthode est appelée, puis MethodInvokeExpr et bien d'autres;
  • Fichier de destination
  • Ligne de destination;
  • Nom de destination;
  • Type de destination.


La source et la destination seront différentes si les éléments de l'ensemble de résultats sont Flow, et vice versa, ils correspondront si le résultat est des éléments de code.

Commencez à créer des requêtes


Toutes les fonctions CMxQL peuvent être divisées en plusieurs types. Ici, à mon avis, on peut noter le principal inconvénient de la documentation CMxQL, toutes les fonctions du dock sont décrites simplement par ordre alphabétique, alors qu'il serait beaucoup plus pratique de les structurer selon la fonctionnalité et seulement ensuite par ordre alphabétique.

  • Fonctions de recherche - presque toutes les fonctions CMxQL avec le nom FindBy * et GetBy * ;
  • Les fonctions des opérations sur les ensembles sont l'addition, la soustraction, l'intersection, l'itération sur les éléments, etc.
  • Fonctions d'analyse - Il s'agit essentiellement des fonctions * InfluencedBy * * InfluencingOn * .


Le principe de base des requêtes est l'alternance de ces types de fonctions. Tout d'abord, en utilisant les fonctions de recherche, nous sélectionnons uniquement les jetons qui nous intéressent par certaines propriétés. En utilisant des opérations sur des ensembles, nous pouvons combiner différents ensembles avec différentes propriétés de jeton en un seul, ou vice versa, soustraire l'autre d'un. Ensuite, en utilisant les fonctions d'analyse, nous construisons le flux de code et essayons de comprendre si les vulnérabilités potentielles dépendent des paramètres aux points d'entrée.

Le choix de l'endroit à partir duquel commencer la recherche, et en général du chemin de recherche entier, dépend du code spécifique, et plus précisément, même du «texte». Dans certains cas, il est pratique de rechercher des requêtes utilisateur à partir du point d'entrée, dans certains cas, il est plus pratique de commencer par la «fin» ou même par le milieu. Tout dépend du code spécifique et vous devez approcher individuellement chaque référentiel.

Exemple: recherche d'injection SQL


Plan de recherche, entre parenthèses, j'ai indiqué le nom des ensembles (variables dans la requête):

  1. Définissez les exceptions - jetons qui peuvent être immédiatement retirés des étendues de recherche ( exclusionList );
  2. Déterminer l'emplacement des contrôles de désinfection / sécurité ( désinfection );
  3. Trouver tous les emplacements de bas niveau avec exécution de requête dans la base de données ( runSuperSecureSQLQuery );
  4. Trouver tous les paramètres des méthodes appelées runSuperSecureSQLQuery ( runSSSQParams );
  5. Trouver des points d'entrée (méthodes parentes et leurs paramètres) pour les lieux d'exécution des requêtes dans la base de données ( entryPointsParameters );
  6. Trouvez les dépendances des paramètres runSSSQParams sur entryPoints , tandis que seuls les endroits où il n'y a pas de nettoyage de la purification d' entrée.


En conséquence, nous obtenons des méthodes de bas niveau avec des requêtes SQL, où les paramètres de la requête SQL:

  • dépendent des paramètres de la méthode;
  • les paramètres sont acceptés sous forme de chaînes;
  • les paramètres sont concaténés à la demande.

Nous ne vérifierons pas si nous pouvons contrôler ces paramètres, car nous pensons qu'il existe un mécanisme pour mapper des variables dans une requête et qu'il existe un transtypage en type numérique pour les nombres, et la concaténation de chaînes est toujours considérée comme dangereuse. Même s'il n'y a aucun contrôle sur la ligne maintenant, elle pourrait bien apparaître dans la nouvelle version.

SQLi: étape 1. Définition des exceptions


Dans les exceptions, vous devez ajouter les classes ou fichiers dans lesquels les noms de jeton peuvent correspondre à ceux que vous recherchez, car ces jetons entraîneront des entrées non valides.

Par exemple, une méthode d'accès à une base de données est appelée runSuperSecureSQLquery . Nous supposons que la méthode runSuperSecureSQLquery à l' intérieur est implémentée en toute sécurité. Et notre tâche est de trouver des endroits où il n'est pas sûr d'utiliser la méthode elle-même. Pour l'injection SQL, les lieux de concaténation des paramètres contrôlés par l'utilisateur ne seront pas des endroits sûrs. Et des emplacements sûrs pour mapper des paramètres dans la structure ORM ou, par exemple, pour des paramètres numériques, il s'agit d'une conversion vers le type correspondant. Nous n'avons pas besoin d'analyser tout le code qui se trouve «plus profondément» que runSuperSecureSQLquery , ce qui signifie qu'il est préférable de l'exclure afin d'éviter les découvertes inutiles.

Pour rechercher de telles exceptions, il est pratique d'utiliser les fonctions CMxQL:
  • FindByFileName () - trouvera l'ensemble de tous les jetons dans un fichier particulier;
  • GetByClass () - trouvera l'ensemble de tous les jetons de la classe avec le nom donné.


Pour une application de test, cette exception est la classe Session , qui contient l'implémentation de la méthode runSuperSecureSQLquery .
Un exemple de demande d'exclusion de code dans la classe Session (la méthode GetByClass () vérifiera lequel des jetons transmis à l'entrée a un type CMx de ClassDecl et émettra beaucoup de jetons de cette classe)

 CxList exclusionList = All.GetByClass(All.FindByName("*Session*")); result = exclusionList; 


Ou une autre façon consiste à jeter du code dans le fichier Session.java entier:

 CxList exclusionList = All.FindByFileName("*Session.java"); result = exclusionList; 


L'astérisque devant le nom est important, car le nom de fichier inclut le chemin d'accès complet.
Nous avons maintenant de nombreux jetons qui peuvent être soustraits dans les prochaines étapes de l'étendue de recherche.

Résultat de la recherche de jetons dans la classe Session :



SQLi: étape 2. Détermination des lieux de désinfection


Il existe 2 méthodes API dans l'application de test (voir une brève description de l'application de test). La différence entre les deux méthodes API est que getTransactionInfo () concatène le paramètre transactionId dans la requête SQL, et getTransactionInfoSecured () convertit d' abord transactionId en Long, puis le transmet sous forme de chaîne. La vulnérabilité (concaténation des paramètres) est intégrée dans les deux méthodes. Mais grâce à la conversion en Long dans getTransactionInfoSecured () , la dernière méthode n'est pas vulnérable à l'injection, car lorsque nous essayons de passer une injection (chaîne), nous obtenons une exception Java.

Dans cet exemple, nous considérerons le plâtre de Long comme le site d'assainissement. Pour trouver ces jetons:

 CxList sanitization = All.FindByName("*Long*"); result = sanitization; 


Exemple de résultat:



Le résultat comprenait des jetons avec les méthodes de type YP Long et getValueAsLong , qui convertissent en interne la valeur en type Long . Vous devez examiner attentivement le résultat pour vous assurer qu'il n'y a rien de plus.

SQLi: étape 3. Rechercher tous les emplacements de bas niveau avec exécution de requête dans la base de données


La requête suivante trouvera tous les emplacements à l'aide du jeton runSuperSecureSQLQuery (utilisé pour accéder à la base de données):

 result = All.FindByName("*runSuperSecureSQLQuery*") 

Résultat de la recherche par nom de jeton runSuperSecureSQLQuery:


De plus, pour les endroits où cette méthode est appelée (classe de facturation ), seuls les jetons d'invocation de méthode (type MethodInvokeExpr ) seront trouvés, et pour le lieu de déclaration de méthode (classe Session ), tous les jetons seront trouvés - variables.

Nous filtrons uniquement les jetons d'appel de méthode:

 CxList runSuperSecureSQLQuery = All.FindByName("*runSuperSecureSQLQuery*").FindByType(typeof(MethodInvokeExpr)); result = runSuperSecureSQLQuery; 

Résultat:


En conséquence, nous avons obtenu 7 places, dont 4 les appels requis à la méthode runSuperSecureSQLQuery () (classes de facturation et d' utilisateur ). 2 - appels à la méthode interne runSuperSecureSQLQuery () à l'intérieur de la classe Session , et une de plus est la méthode add , qui est plutôt une sorte de bizarrerie de recherche CMxQL. Disons simplement que je ne m'attendais pas à ce qu'il soit dans la liste =) Les jetons de la classe Session , comme nous l'avons découvert à l'étape 1, ne sont pas intéressants pour nous, nous allons donc simplement les soustraire du résultat:

 CxList runSuperSecureSQLQuery = All.FindByName("*runSuperSecureSQLQuery*").FindByType(typeof(MethodInvokeExpr)); result = runSuperSecureSQLQuery - exclusionList; 

Nous obtenons une liste valide d'appels à la méthode requise:



Notez les fonctions FindByType () et typeof () dans la requête précédente. Si nous voulons rechercher par type CMx, c'est-à-dire par la propriété CxList «Source Type» - alors nous utilisons typeof (Source Type) . Si nous voulons faire une recherche par type de données, nous devons passer le paramètre comme une chaîne. Par exemple:

 result = All.FindByType("String"); 

trouvera tous les jetons java de type String.

SQLi: étape 4. Trouver tous les paramètres des méthodes appelées runSuperSecureSQLQuery


Pour rechercher des paramètres de méthode, la fonction CMxQL GetParameters () est utilisée :

 CxList runSSSQParams = All.GetParameters(runSuperSecureSQLQuery); result = runSSSQParams; 

Résultat:



SQLi: étape 5. Rechercher des points d'entrée pour les emplacements d'exécution des requêtes dans la base de données


Pour ce faire, nous obtenons d'abord les noms des méthodes parentes, à l'intérieur desquelles se trouvent les appels à la base de données runSuperSecureSQLQuery , puis nous obtenons leurs paramètres. Pour rechercher des jetons parents, la fonction CMxQL GetAncOfType () est utilisée :

 CxList entryPoints = runSuperSecureSQLQuery.GetAncOfType(typeof(MethodDecl)); result = entryPoints; 


Dans cette requête, pour l'ensemble runSuperSecureSQLQuery, renvoyez tous les jetons parents de type MethodDecl - il s'agit de la méthode précédente dans la pile des appels:



Pour rechercher des paramètres de méthode, nous utilisons également GetParameters () :

 CxList entryPointsParameters = All.GetParameters(entryPoints).FindByType("String"); 


La requête renverra les paramètres d'un sous-ensemble de entryPoints avec le type Java String:



SQLi: étape 6. Recherchez les dépendances des paramètres runSSSQParams sur entryPointsParameters, alors que seuls les endroits où il n'y a pas d'entrée d'entrée de nettoyage


Dans cette étape, nous utilisons les fonctions d'analyse. Les fonctions suivantes sont utilisées pour analyser le code Flow:

  • InfluencedBy ()
  • InfluencedByAndNotSanitized ()
  • InfluencingOn ()
  • InfluencingOnAndNotSanitized ()
  • NotInfluencedBy ()
  • NotInfluencingOn ()


Pour rechercher les paramètres de demande de flux runSSSQParams en fonction des paramètres de la méthode parent entryPointsParameters et exclure les jetons d'assainissement:

 CxList dataInflOnTable = runSSSQParams.InfluencedByAndNotSanitized(entryPointsParameters, sanitization); 


Cependant, je ne sais pas si les fonctions * AndNotSanitized à l'intérieur font de la magie, et il semble plus que la méthode soustrait simplement l'ensemble aseptisé de son résultat. Autrement dit, si vous le faites:

 CxList dataInflOnTable = runSSSQParams.InfluencedBy(entryPointsParameters) - sanitization; 


il se passe la même chose. Même si je n'ai peut-être pas trouvé d'option alors qu'il y a encore des différences.

Le résultat de la requête nous donne un flux correctement construit:



Got Flow avec injection SQL potentielle. Comme le montre la capture d'écran, Checkmarx a renvoyé 3 Flow. Le flux dans la capture d'écran est le plus court, il commence et se termine dans un fichier et une méthode. Le flux suivant part déjà dans la classe Session. Faites attention à la source / destination. Et la dernière est une autre méthode de la classe Session. Le flux à l'intérieur de la session ressemblera à ceci:



Pour sélectionner un flux, la méthode ReduceFlow (CxList.ReduceFlowType flowType) est utilisée , où flowType peut être:

  • CxList.ReduceFlowType.ReduceBigFlow - sélectionnez le flux le plus court
  • CxList.ReduceFlowType.ReduceSmallFlow - sélectionnez le flux le plus long


SQLi: requête finale pour trouver une injection SQL


 // 1.   CxList exclusionList = All.GetByClass(All.FindByName("*Session*")); // 2.    CxList sanitization = All.FindByName("*Long*"); // 3.    runSuperSecureSQLQuery() CxList runSuperSecureSQLQuery = All.FindByName("*runSuperSecureSQLQuery*").FindByType(typeof(MethodInvokeExpr)); runSuperSecureSQLQuery -= exclusionList; // 4.     runSuperSecureSQLQuery() CxList runSSSQParams = All.GetParameters(runSuperSecureSQLQuery); // 5.   ,     runSuperSecureSQLQuery() CxList entryPoints = runSuperSecureSQLQuery.GetAncOfType(typeof(MethodDecl)); CxList entryPointsParameters = All.GetParameters(entryPoints).FindByType("String"); // 6.       (runSuperSecureSQLQuery)     CxList dataInflOnTable = runSSSQParams.InfluencedByAndNotSanitized(entryPointsParameters, sanitization); // 7.   result = dataInflOnTable.ReduceFlow(CxList.ReduceFlowType.ReduceBigFlow); 


Exemple 2: recherche de références d'objet direct non sécurisées


Dans cette demande, nous rechercherons tous les endroits où le travail avec les objets a lieu sans vérifier le propriétaire de l'objet. Dans ce cas, différents noms de paramètres HTTP pour l'identifiant de boîte aux lettres peuvent être utilisés (nous supposons qu'il s'agit de Legacy), et la vérification elle-même peut avoir lieu à différentes étapes: quelque part juste au point API d'entrée HTTP, quelque part avant la demande à la base de données, et parfois dans les méthodes intermédiaires.

Plan de recherche
  1. Définissez les exceptions ( exclusionList );
  2. Identifier les lieux de contrôle des autorisations ( idorSanitizer );
  3. Trouver des points d'entrée - lieux pour le traitement principal des requêtes HTTP ( webRemoteMethods );
  4. Uniquement par des jetons de point d'entrée pour trouver l'emplacement d'extraction du paramètre HTTP mailboxid ( mailboxidInit );
  5. Trouver tous les appels de webRemoteMethods vers les méthodes middleware et les paramètres de ces appels ( middlewareMethods );
  6. Trouvez des méthodes de middleware qui dépendent de l'identifiant de boîte aux lettres ( apiPotentialIDOR );
  7. Trouver tous les endroits où les méthodes middleware sont définies ( middlewareDecl );
  8. Parcourez tous les apiPotentialIDOR et sélectionnez uniquement les middlewareDecl dans lesquels il n'y a pas de vérification du propriétaire de l'objet boîte aux lettres .


IDOR: Étape 1. Identifier les exceptions


Dans ce cas, excluez tous les jetons d'un fichier spécifique:

 CxList exclusionList = All.FindByFileName("*WebMethodContext.java"); result = exclusionList; 

WebMethodContext.java contient une implémentation de méthodes telles que getMailboxId et getUserId , ainsi que la chaîne "mailboxid". Étant donné que le nom des jetons coïncidera avec ceux dont nous avons besoin pour rechercher les vulnérabilités, ce fichier émettra de fausses constatations.

IDOR: Étape 2. Recherchez les contrôles d'autorisation


Dans l'application de test, la méthode validateMailbox () est utilisée pour déterminer si l'objet demandé appartient à l'utilisateur:

 CxList idorSanitizer = All.FindByName("*validateMailbox*"); result = idorSanitizer; 

Résultat:



IDOR: étape 3. Rechercher des points d'entrée pour les requêtes API HTTP personnalisées


Les gestionnaires de requêtes HTTP ont une annotation spéciale qui les rend faciles à trouver. Dans mon cas, il s'agit de «WebRemote», la fonction CMxQL FindByCustomAttribute () est utilisée pour rechercher des annotations. Pour FindByCustomAttribute () , la fonction de recherche du jeton parent GetAncOfType () renverra la méthode sous l'annotation:

 CxList webRemoteMethods = All.FindByCustomAttribute("WebRemote") .GetAncOfType(typeof(MethodDecl)); result = webRemoteMethods; 


Résultat de la demande:



IDOR: étape 4. À l'aide des jetons de point d'entrée uniquement, recherchez les emplacements d'extraction HTTP pour le paramètre mailboxid


Pour rechercher des jetons liés au traitement du paramètre HTTP mailboxid:

 CxList getMailboxId = All.FindByName("\"mailboxId\"") + All.FindByName("\"mid\"") + All.FindByName("\"boxid\""); result = getMailboxId; 

nous avons ajouté 3 sets avec 3 lignes différentes, car selon la légende, le nom du paramètre HTTP peut différer selon les différentes parties du système.

La requête trouvera tous les endroits où la boîte aux lettres / mid / boxid est écrite sous forme de chaîne (entre guillemets). Mais cette requête renverra beaucoup de trouvailles, tk. une telle chaîne peut être trouvée non seulement aux endroits où les paramètres HTTP sont extraits. Si nous continuons à travailler avec cet ensemble, nous obtiendrons un grand nombre de fausses découvertes.

Par conséquent, nous ne rechercherons que des jetons de points d'entrée ( webRemoteMethods ). Pour trouver tous les jetons enfants, la fonction CMBQL GetByAncs () est utilisée :

 result = All.GetByAncs(webRemoteMethods); 

La demande renverra tous les jetons appartenant aux méthodes annotées en tant que WebRemote . Déjà à ce stade, nous pouvons filtrer les jetons des méthodes dans lesquelles le propriétaire de l'objet est vérifié. Par conséquent, nous réécrivons la requête précédente pour rechercher des jetons enfants de manière à sélectionner uniquement les jetons enfants des méthodes WebRemote , où il n'y a pas de contrôle de sécurité pour le propriétaire de l'objet. Pour ce faire, utilisez une boucle avec la condition:

 //          CxList entry_point_tokens = All.NewCxList(); //      webRemoteMethods foreach (CxList method in webRemoteMethods) { //        CxList method_tokens = All.GetByAncs(method); // ,       ,    owner if (method_tokens.FindByName(idorSanitizer).Count > 0) { //  ,     , ,     } else { //  ,         entry_point_tokens.Add(method_tokens); } } 

Nous pouvons maintenant effectuer une sélection plus précise à l'aide des paramètres HTTP mailboxid :

 CxList getMailboxHTTPParams = entry_point_tokens.FindByName("\"mailboxid\"") + entry_point_tokens.FindByName("\"mid\"") + entry_point_tokens.FindByName("\"boxid\""); result = getMailboxHTTPParams; 

Mais nous ne nous intéressons pas aux endroits où les paramètres HTTP sont obtenus, mais aux variables auxquelles sont finalement attribuées les valeurs des paramètres HTTP. Puisqu'il est plus fiable de rechercher Flow précisément avec des jetons de variables.

La fonction CMxQL FindByInitialization () trouvera les lieux d'initialisation des variables pour les jetons donnés:

 CxList mailboxidInit = entry_point_tokens.FindByInitialization(getMailboxHTTPParams); result = mailboxidInit; 

Résultat:



IDOR: étape 5. Recherchez tous les appels de webRemoteMethods vers les méthodes et paramètres middleware de ces appels


Par middleware, j'entends un code qui va plus loin que les méthodes de traitement des requêtes API HTTP, c'est-à-dire plus profondément que les points d'entrée des requêtes des utilisateurs. Par exemple, pour la capture d'écran ci-dessus, il s'agit des méthodes de la classe User , des appels à user.getSettings () et user.getSecureSettings () :

 CxList middlewareMethods = All.FindByShortName("user").GetRightmostMember(); CxList middlewareMethodsParams = entry_point_tokens.GetParameters(middlewareMethods); result = middlewareMethodsParams; 

Tout d'abord, nous sélectionnons tous les jetons avec le nom d'utilisateur, puis en utilisant GetRightmostMember (), nous sélectionnons les jetons d'appel pour le middleware. GetRightmostMember () dans la chaîne d'appels de méthode renverra celui le plus à droite. Ensuite, nous dérivons les paramètres de la méthode trouvée en utilisant GetParameters () .

Résultat:



IDOR: étape 6. Rechercher des méthodes middleware qui dépendent de l'identifiant de boîte aux lettres


L'analyse de flux utilise les méthodes * InfluencedBy * et * InfluncingOn * . La différence entre eux est claire de nom.

Par exemple:

 All.InfluencedBy(getMailboxHTTPParams) 

passera par l'ensemble Tous et trouvera tous les jetons qui dépendent de getMailboxHTTPParams .

La même chose peut être écrite d'une autre manière:

 getMailboxHTTPParams.InfluencingOn(All) 


Pour rechercher des jetons dépendants de mailboxidInit :

 CxList apiPotentialIDOR = entry_point_tokens.InfluencedByAndNotSanitized(mailboxidInit, idorSanitizer); result = apiPotentialIDOR; 

Résultat:



IDOR: Étape 7. Trouvez tous les endroits pour définir les méthodes du middleware


Trouvons les définitions de toutes les méthodes intermédiaires qui peuvent être utilisées dans les endroits où les demandes des utilisateurs sont traitées. Pour ce faire, nous mettons en évidence leur propriété commune, par exemple, dans toutes ces méthodes, il y a la création d'un objet Request () , la création d'un objet est de type CMx ObjectCreateExpr :

 CxList requests = (All - exclusionList).FindByType(typeof(ObjectCreateExpr)).FindByName("*Request*"); CxList middlewareDecl = requests.GetAncOfType(typeof(MethodDecl)); result = middlewareDecl; 


(All - exclusionList) - vous pouvez effectuer cette soustraction d'ensembles, puis appeler la fonction CMxQL souhaitée à partir du résultat. Les requêtes contiennent désormais tous les jetons avec le nom Request et le type correspondant à la création de l'objet.

Ensuite, en utilisant le GetAncOfType () familier , nous trouvons le jeton parent de type MethodDecl .

Résultat:



IDOR: Étape 8. Parcourez tous les apiPotentialIDOR et sélectionnez uniquement les middlewareDecl dans lesquels il n'y a pas de vérification du propriétaire de l'objet mailboxid


Dans la dernière partie de la demande, nous déterminerons lesquelles des méthodes middleware sont appelées directement à partir des méthodes du point d'entrée et ne vérifions pas à qui appartient l' ID de boîte aux lettres . Combinez ensuite Flow pour une analyse plus pratique des résultats.

Nouvelles fonctionnalités que nous n'avons pas encore utilisées:
GetCxListByPath () - cette fonction est nécessaire pour itérer sur Flow, si elle n'est PAS utilisée, CMx compressera le flux dans l'élément de code (dans le premier nœud de flux)
Concaténer * () - un certain nombre de fonctions nécessaires pour combiner plusieurs flux en un seul
FindByParameters () - recherche une méthode par un jeton de paramètre spécifique
GetName () - retournera une chaîne avec le nom du jeton, s'il y a plus d'un élément dans CxList, alors il retournera le premier. La méthode est utilisée uniquement lors de l'itération sur les éléments d'un ensemble.

La dernière partie de la demande:

 //    CxList vulns = All.NewCxList(); //   Flow  apiPotentialIDOR foreach(CxList cxFlow in apiPotentialIDOR.GetCxListByPath()) { //    Flow CxList endNode = cxFlow.GetStartAndEndNodes(CxList.GetStartEndNodesType.EndNodesOnly); //       flow (mailboxid) CxList method_call = entry_point_tokens.FindByParameters(endNode); //     CxList method_decl = middlewareDecl.FindByShortName(method_call.GetName()); //     if (method_decl.Count > 0) { //       CxList _all = (All - exclusionList).GetByAncs(method_decl); //       if (_all.FindByName(idorSanitizer).Count > 0) { //  ,       cxLog.WriteDebugMessage("find sanitized in method: " + method_call.GetName()); //  ,   Flow     vulns } else { //     Flow       vulns.Add(cxFlow.ConcatenatePath(method_call).ConcatenatePath(method_decl)); cxLog.WriteDebugMessage("find NOT sanitized in method: " + method_call.GetName()); } } } 


Résultat:



CocatenatePath , . Code Element Flow

IDOR: IDOR


 // 1.   CxList exclusionList = All.FindByFileName("*WebMethodContext.java"); // 2.     CxList idorSanitizer = All.FindByName("*validateMailbox*"); // 3.    –    HTTP  CxList webRemoteMethods = All.FindByCustomAttribute("WebRemote").GetAncOfType(typeof(MethodDecl)); // 4.         HTTP  mailboxid //     CxList entry_point_tokens = All.NewCxList(); foreach (CxList method in webRemoteMethods) { CxList method_tokens = All.GetByAncs(method); if (method_tokens.FindByName(idorSanitizer).Count > 0) { } else { entry_point_tokens.Add(method_tokens); } } //    HTTP    -  CxList getMailboxHTTPParams = entry_point_tokens.FindByName("\"mailboxId\"") + entry_point_tokens.FindByName("\"mid\"") + entry_point_tokens.FindByName("\"boxid\""); CxList mailboxidInit = entry_point_tokens.FindByInitialization(getMailboxHTTPParams); // 5.      middleware     CxList middlewareMethods = All.FindByShortName("user").GetRightmostMember(); CxList middlewareMethodsParams = entry_point_tokens.GetParameters(middlewareMethods); // 6.  middleware ,     mailboxid CxList apiPotentialIDOR = entry_point_tokens.InfluencedByAndNotSanitized(mailboxidInit, idorSanitizer); // 7.      middleware      CxList requests = (All - exclusionList).FindByType(typeof(ObjectCreateExpr)).FindByName("*Request*"); CxList middlewareDecl = requests.GetAncOfType(typeof(MethodDecl)); // 8.    apiPotentialIDOR     middlewareDecl,      CxList vulns = All.NewCxList(); foreach(CxList cxFlow in apiPotentialIDOR.GetCxListByPath()) { CxList endNode = cxFlow.GetStartAndEndNodes(CxList.GetStartEndNodesType.EndNodesOnly); CxList method_call = entry_point_tokens.FindByParameters(endNode); CxList method_decl = middlewareDecl.FindByShortName(method_call.GetName()); if (method_decl.Count > 0) { CxList _all = (All - exclusionList).GetByAncs(method_decl); if (_all.FindByName(idorSanitizer).Count > 0) { cxLog.WriteDebugMessage("find sanitized in method: " + method_call.GetName()); } else { vulns.Add(cxFlow.ConcatenatePath(method_call).ConcatenatePath(method_decl)); cxLog.WriteDebugMessage("find NOT sanitized in method: " + method_call.GetName()); } } } result = vulns; 


Conclusion


Checkmarx , . , , , .. Flow ( ). , , «» .

false positive, :
  • , ( ).
  • , ( ). , «Privacy Violation», , , Web UI. , .. UI . TLS XSS .
  • - , (, ). , XXE , , - , .
  • false positive, , CMxQL FindBy/GetBy. , ( SQL).
  • false positives, , , , , CMx, . , LDAP , . c LDAP- , , .


how-to «hello world» , Checkmarx.

Source: https://habr.com/ru/post/fr477742/


All Articles