"Hallo Checkmarx!" Wie schreibe ich eine Anfrage für Checkmarx SAST und finde coole Schwachstellen



Hallo habr

In dem Artikel möchte ich über unsere Erfahrungen bei der Erstellung meiner Abfragen in Checkmarx SAST sprechen.

Wenn Sie sich zum ersten Mal mit diesem Analysegerät vertraut machen, haben Sie möglicherweise den Eindruck, dass es nicht nur nach schwachen Verschlüsselungs- / Hashing-Algorithmen und einer Menge falsch positiver Ergebnisse sucht, sondern auch nichts anderes zurückgibt. Aber wenn es richtig konfiguriert ist, ist es ein sehr leistungsfähiges Tool, das nach schweren Fehlern suchen kann.

Wir werden die Feinheiten der Checkmarx SAST-Abfragesprache verstehen und 2 Abfragen schreiben, um nach SQL-Injections und Insecure Direct Object References zu suchen.


Eintrag


Nach langem Suchen nach Anleitungen oder Artikeln zu Checkmarx wurde mir klar, dass es neben der offiziellen Dokumentation nicht genügend nützliche Informationen gab. Und die offizielle Dokumentation besagt nicht, dass alles sehr klar und verständlich wird. Zum Beispiel konnte ich keine Best Practices finden, wie man Überschreibungsabfragen richtig organisiert, wie man Abfragen "für Dummies" schreibt usw. Ja, es gibt eine Dokumentation zu den CMx Query Language-Funktionen, aber hier ist, wie man diese Funktionen in einer einzigen Abfrage kombiniert. Die Dokumentation ist nicht geschrieben.

Vielleicht ist das Fehlen von Artikeln und Anleitungen aus der Checkmarx-Community mit den hohen Kosten des Tools und demzufolge einem kleinen Publikum verbunden. Oder vielleicht kümmern sich nur wenige Leute um die Feinabstimmung und verwenden die Lösung wie sie ist.

Meiner Erfahrung nach wird SAST eher zur Erfüllung von Formalitäten im Zusammenhang mit verschiedenen Kundenanforderungen verwendet, als zur Suche nach echten Fehlern. Infolgedessen haben wir bestenfalls eine relativ kleine Anzahl von „Schwachstellen“, die fast automatisch als „nicht ausnutzbar“ bezeichnet werden (da dies in 99,9% der Fälle der Fall ist).

Es sollte beachtet werden, dass Checkmarx selbst versucht, ihre Abfragen zu aktualisieren, damit sie sofort das beste Ergebnis liefern. CMx Query Language-Abfragen sind jedoch auf den „allgemeinen Fall“ zugeschnitten. Die anfängliche Suche nach Tokens basiert auf dem Namen. CMx SAST geht beispielsweise davon aus, dass alle Abfragen an die Datenbank folgendermaßen aussehen: * createQuery * oder * createSQLQuery *. Wenn jedoch die interne Entwicklung für die Arbeit mit der Datenbank verwendet wird und die Methode zum Abfragen der Datenbank anders aufgerufen wird, z. B. * driveMyQuery *, werden alle SQL-Methoden übersprungen. Beispielsweise verwendet unser Kunde benutzerdefiniertes ORM für SQL DB. In diesem Fall haben CMx-Abfragen alle SQL-Injections übersprungen.

Abkürzungen und Definitionen


CMx - Checkmarx SAST.
CMxQL - Checkmarx SAST-Abfragesprache
Token - Ein String mit einem bestimmten Wert ist das Ergebnis der Arbeit des lexikalischen Analysators (auch Tokenisierung genannt).

Anwendung testen


Um einen Artikel zu schreiben, skizzierte ich Java-Code, eine kleine Testanwendung. Dieser Code ist eine ungefähre Kopie eines kleinen Teils des realen Systems. Obwohl sich der Code der Testanwendung im Allgemeinen von keinem anderen HTTP-Backend-Code stark unterscheidet. Wichtige Abschnitte des Testanwendungscodes werden in den Screenshots angezeigt.

Die Testanwendung hat folgenden Aufbau


WebRouter- Klasse zur Verarbeitung eingehender HTTP-Anfragen, 4 Methoden zur Verarbeitung von URLs:
  • / getTransaction - akzeptiert die Transaktions- ID bei der Eingabe und gibt die Informationen dazu zurück, nimmt sie als Zeichenfolge und übergibt sie an getTransactionInfo (transactionId) => getTransactionInfo (transactoinId ).
  • / getSecureTransaction - akzeptiert die Transaktions- ID als Eingabe und gibt die Informationen dazu zurück. id nimmt sie als Zeichenfolge und übergibt sie Fall, dass die Injektion nicht ausgenutzt wird);
  • / getSettings - akzeptiert die Benutzer- ID und die Postfach- ID als Eingabe - und gibt die Postfacheinstellungen aus. Überprüft nicht, ob die Mailbox-ID dem Benutzer gehört.
  • / getSecureSettings - akzeptiert auch die Benutzer- ID und die Postfach- ID für die Eingabe und zeigt die Postfacheinstellungen an. ABER prüft, ob die Mailbox-ID dem Benutzer gehört.


CMx: Allgemeine Informationen und grundlegende Definitionen


Bevor Sie mit der Entwicklung von Abfragen beginnen


Die Entwicklung von Abfragen erfolgt in einem separaten Programm CxAuditor. In CxAuditor müssen Sie den gesamten Code scannen (lokales Projekt erstellen), für das wir Abfragen schreiben. Danach können Sie neue Abfragen schreiben und ausführen. Bei einer großen Codebasis kann das primäre Scannen Stunden dauern und Gigabyte an Speicher beanspruchen. Danach wird jede Anfrage nicht schnell genug ausgeführt. Dies ist für die Entwicklung völlig ungeeignet.

Aus diesem Grund können Sie eine kleine Menge von Dateien aus dem Projekt entnehmen, idealerweise mit einem Fehler, der im Code vor dem Typ gefunden wurde, unter dem die Anforderung geschrieben wird (oder den Fehler manuell dort abgelegt), und nur diese Dateien durchsuchen. Es ist nicht erforderlich, die Dateistruktur des Projekts einzuhalten. Das heißt, wenn Sie über Java-Paket A und B verfügen und die Klassen in Paket B die Klassen und Methoden von Paket A verwenden, können Sie all dies in ein einziges Verzeichnis packen, und CMx wird die Beziehungen und Aufrufketten zwischen Dateien immer noch richtig (gut oder fast) verstehen immer korrekt, obwohl Fehler kaum mit der Dateistruktur des Projekts zusammenhängen).

Grundlegende Definitionen


Cxlist


Der Hauptdatentyp in CMx. Das Ergebnis fast aller CMxQL-Funktionen ist CxList . Dies sind viele Elemente mit bestimmten Eigenschaften. Die Eigenschaften, die für die Entwicklung am nützlichsten sind, werden unten betrachtet.

Ergebnis


CMxQL hat ein eingebautes variables Ergebnis . Die Menge, die die Ergebnisvariable enthält, wird nach Ausführung der gesamten Abfrage als Ergebnis angezeigt.

Das heißt, die letzte Operation einer Abfrage sollte das Zeichenfolgenergebnis = WHATEVER sein , zum Beispiel:
result = All.FindByName("anyname"); 

Flow- und Code-Element


Die meisten CMxQL-Funktionen werden nach dem Typ der zurückgegebenen Werte in zwei Funktionen unterteilt, die "Codeelemente" zurückgeben und die Flow zurückgeben. In beiden Fällen ist das Ergebnis eine CxList . Der Inhalt unterscheidet sich jedoch geringfügig für Flow- und Code-Elemente.
  • Codeelement - Token - zum Beispiel eine Variable, ein Methodenaufruf, eine Zuweisung usw .;
  • Flow - die Beziehung zwischen den gegebenen Token.


Alle und "Unter" Alle


Jede CMxQL-Funktion kann entweder für die Gruppe " Alle" (sie enthält alle Token des gesamten gescannten Codes, wir haben bereits ein Beispiel mit Ergebnis gesehen ) oder für die Gruppe "CxList" ausgeführt werden , die wiederum als Ergebnis einiger Operationen in der Abfrage abgerufen wurde, beispielsweise der Abfrage:
 CxList newList = CxList.New(); 

erstellt eine leere Menge, die wir dann mit der Add () -Methode mit Elementen füllen und dann bereits nach den Elementen der neuen Menge suchen können:
 CxList newFind = newList.FindByName("narrowedScope"); 

Eigenschaften der gefundenen Gegenstände


Jedes Element der CxList-Gruppe verfügt über mehrere Eigenschaften. Bei der Analyse der Ergebnisse zum Schreiben von Abfragen sind die folgenden am nützlichsten:

  • SourceFile - der Name der Datei, die dieses Element enthält;
  • Source Line - Zeilennummer mit Token;
  • Quellenname - Der Name des Tokens. Entspricht dem Token, d. H. Wenn die Variable var1 heißt, ist der Quellenname = var1;
  • Quelltyp - Der Typ des Tokens. Wenn es sich beispielsweise um eine Zeichenfolge handelt, ist dies StringLiteral, wenn die Methode aufgerufen wird, MethodInvokeExpr und viele andere.
  • Zieldatei
  • Ziellinie;
  • Zielname;
  • Zieltyp.


Quelle und Ziel unterscheiden sich, wenn die Elemente der Ergebnismenge Flow sind, und umgekehrt, wenn das Ergebnis Codeelemente sind.

Erstellen Sie Abfragen


Alle CMxQL-Funktionen können in verschiedene Typen unterteilt werden. Hier kann man meiner Meinung nach den Hauptnachteil der CMxQL-Dokumentation bemerken, alle Funktionen im Dock werden einfach in alphabetischer Reihenfolge beschrieben, während es viel bequemer wäre, sie nach der Funktionalität und nur dann alphabetisch zu strukturieren.

  • Suchfunktionen - fast alle CMxQL-Funktionen mit den Namen FindBy * und GetBy * ;
  • Die Funktionen von Operationen auf Mengen sind Addition, Subtraktion, Schnittmenge, Iteration über Elemente usw.
  • Analysefunktionen - Dies sind im Grunde * InfluencedBy * * InfluencingOn * -Funktionen.


Das Grundprinzip von Abfragen ist die Abwechslung dieser Arten von Funktionen. Zunächst wählen wir mithilfe der Suchfunktionen nur die Token aus, die uns aufgrund bestimmter Eigenschaften interessieren. Mit Operationen auf Mengen können wir verschiedene Mengen mit verschiedenen Token-Eigenschaften zu einer kombinieren oder umgekehrt die andere von einer subtrahieren. Anschließend erstellen wir mithilfe der Analysefunktionen den Code Flow und versuchen zu verstehen, ob die potenziellen Schwachstellen von den Parametern an den Einstiegspunkten abhängen.

Die Wahl des Ortes, von dem aus die Suche gestartet werden soll, und im Allgemeinen der gesamte Suchpfad, hängt vom spezifischen Code ab, genauer gesagt sogar vom „Text“. In einigen Fällen ist es bequem, vom Einstiegspunkt aus nach Benutzerabfragen zu suchen, in einigen Fällen ist es bequemer, vom „Ende“ oder sogar von der Mitte aus zu beginnen. Es hängt alles vom spezifischen Code ab und Sie müssen jedes Repository einzeln ansprechen.

Beispiel: SQL-Injection durchsuchen


Suchplan, in Klammern habe ich den Namen der Mengen angegeben (Variablen in der Abfrage):

  1. Ausnahmen definieren - Token, die sofort aus dem Suchbereich entfernt werden können ( exclusionList );
  2. Bestimmen Sie den Ort der Desinfektion / Sicherheitskontrollen ( Desinfektion );
  3. Finde alle Low-Level-Stellen mit Abfrageausführung in der Datenbank ( runSuperSecureSQLQuery );
  4. Finde alle Parameter der aufgerufenen Methoden runSuperSecureSQLQuery ( runSSSQParams );
  5. Suchen von Einstiegspunkten (übergeordnete Methoden und deren Parameter) für die Orte der Abfrageausführung in der Datenbank ( entryPointsParameters );
  6. Ermitteln Sie die Abhängigkeiten von runSSSQParams- Parametern von entryPoints , während nur die Stellen angezeigt werden , an denen keine Bereinigung der Eingabebereinigung erfolgt .


Als Ergebnis erhalten wir Low-Level-Methoden mit SQL-Abfragen, bei denen die Parameter der SQL-Abfrage:

  • hängen von den Parametern der Methode ab;
  • Parameter werden als Zeichenfolgen akzeptiert.
  • Parameter werden mit der Anforderung verknüpft.

Wir werden nicht prüfen, ob wir diese Parameter kontrollieren können Wir glauben, dass es einen Mechanismus zum Zuordnen von Variablen zu einer Abfrage gibt und dass Zahlen in einen numerischen Typ umgewandelt werden. Die Verkettung von Zeichenfolgen wird immer als gefährlich angesehen. Auch wenn die Leitung derzeit nicht kontrolliert wird, wird sie möglicherweise in der neuen Version angezeigt.

SQLi: Schritt 1. Ausnahmen definieren


In Ausnahmefällen müssen Sie die Klassen oder Dateien hinzufügen, deren Tokennamen mit den gesuchten übereinstimmen können, weil Diese Token führen zu ungültigen Einträgen.

Eine Methode für den Zugriff auf eine Datenbank heißt beispielsweise runSuperSecureSQLquery . Wir gehen davon aus, dass die darin enthaltene Methode runSuperSecureSQLquery sicher implementiert ist. Und unsere Aufgabe ist es, Orte zu finden, an denen es nicht sicher ist, die Methode selbst anzuwenden. Bei der SQL-Injection sind Orte der Verkettung von benutzergesteuerten Parametern keine sicheren Orte. Und sichere Orte für die Zuordnung von Parametern in die ORM-Struktur oder zum Beispiel für numerische Parameter, dies ist eine Umwandlung in den entsprechenden Typ. Wir müssen nicht den gesamten Code scannen, der "tiefer" liegt als runSuperSecureSQLquery . Das heißt, es ist besser, ihn auszuschließen, um unnötige Fundstellen zu vermeiden.

Um nach solchen Ausnahmen zu suchen, ist es zweckmäßig, die CMxQL-Funktionen zu verwenden:
  • FindByFileName () - Findet die Menge aller Token in einer bestimmten Datei.
  • GetByClass () - Findet die Menge aller Token in der Klasse mit dem angegebenen Namen.


Bei einer Testanwendung ist dies die Session- Klasse, die die Implementierung der runSuperSecureSQLquery- Methode enthält.
Ein Beispiel für eine Anforderung zum Ausschließen von Code in der Session- Klasse (die GetByClass () -Methode überprüft, welche der an die Eingabe übergebenen Token einen CMx-Typ von ClassDecl aufweisen und viele Token dieser Klasse ausgeben).

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


Eine andere Möglichkeit besteht darin, Code in die gesamte Session.java- Datei zu werfen:

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


Das Sternchen vor dem Namen ist wichtig, da der Dateiname den gesamten Pfad enthält.
Jetzt haben wir viele Token, die in den nächsten Schritten vom Suchbereich abgezogen werden können.

Das Ergebnis der Suche nach Tokens in der Session- Klasse:



SQLi: Schritt 2. Bestimmen der Desinfektionsorte


Die Testanwendung enthält 2 API-Methoden (siehe kurze Beschreibung der Testanwendung). Der Unterschied zwischen den beiden API-Methoden besteht darin, dass getTransactionInfo () den transactionId-Parameter in der SQL-Abfrage verkettet und getTransactionInfoSecured () zuerst transactionId in Long konvertiert und dann als Zeichenfolge übergibt. Sicherheitslücke (Parameterverkettung) ist in beide Methoden eingebettet. Dank dem Casting auf Long in getTransactionInfoSecured () ist die letzte Methode jedoch nicht anfällig für Injection, da beim Versuch, eine Injection (String) zu übergeben, eine Java-Ausnahme auftritt.

In diesem Beispiel betrachten wir die Besetzung von Long als Sanitärstandort. So finden Sie diese Token:

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


Beispielergebnis:



Das Ergebnis enthielt Token mit den YP-Methoden Long und getValueAsLong , die den Wert intern in den Typ Long konvertieren . Sie müssen das Ergebnis sorgfältig prüfen, um sicherzustellen, dass keine zusätzlichen Informationen vorhanden sind.

SQLi: Schritt 3. Finden Sie alle Low-Level-Stellen mit Abfrageausführung in der Datenbank


Die folgende Abfrage findet alle Orte mit dem runSuperSecureSQLQuery-Token (mit dem auf die Datenbank zugegriffen wird):

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

Suchergebnis nach Tokenname runSuperSecureSQLQuery:


Außerdem werden für Stellen, an denen diese Methode aufgerufen wird ( Abrechnungsklasse ), nur Methodenaufruftoken (Typ MethodInvokeExpr ) und für die Methodendeklarationsstelle ( Sitzungsklasse ) auch alle Token - Variablen gefunden.

Wir filtern nur die Methodenaufruftoken heraus:

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

Ergebnis:


Als Ergebnis erhielten wir 7 Stellen, davon 4 die erforderlichen Aufrufe der runSuperSecureSQLQuery () -Methode ( Billing- und User- Klassen). 2 - ruft die interne Methode runSuperSecureSQLQuery () in der Session- Klasse auf, und eine weitere Methode ist die add- Methode, bei der es sich eher um eine CMxQL-Suche handelt. Sagen wir einfach, dass ich nicht damit gerechnet habe, dass es in der Liste enthalten ist =) Die Token in der Session- Klasse, wie wir in Schritt 1 herausgefunden haben, sind für uns nicht interessant, deshalb werden wir sie einfach vom Ergebnis abziehen:

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

Wir erhalten eine gültige Liste der Aufrufe der gewünschten Methode:



Beachten Sie die Funktionen FindByType () und typeof () in der vorherigen Abfrage. Wenn wir nach CMx-Typ suchen möchten, dh nach der CxList- Eigenschaft „Source Type“ - dann verwenden wir typeof (Source Type) . Wenn wir eine Suche nach Datentyp durchführen möchten, müssen wir den Parameter nur als Zeichenfolge übergeben. Zum Beispiel:

 result = All.FindByType("String"); 

findet alle Java-Token mit dem Typ String.

SQLi: Schritt 4. Suchen Sie alle Parameter der runSuperSecureSQLQuery-Methode


Zur Suche nach Methodenparametern wird die CMxQL-Funktion GetParameters () verwendet :

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

Ergebnis:



SQLi: Schritt 5. Suchen Sie Einstiegspunkte für Abfrageausführungsorte in der Datenbank


Dazu erhalten wir zuerst die Namen der übergeordneten Methoden, in denen sich die Aufrufe der runSuperSecureSQLQuery- Datenbank befinden, und dann deren Parameter. Um nach übergeordneten Token zu suchen, wird die CMxQL-Funktion GetAncOfType () verwendet :

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


Geben Sie in dieser Abfrage für die Gruppe runSuperSecureSQLQuery alle übergeordneten Token des Typs MethodDecl zurück. Dies ist die vorherige Methode im Aufrufstapel:



Zur Suche nach Methodenparametern verwenden wir auch GetParameters () :

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


Die Abfrage gibt die Parameter einer Teilmenge von entryPoints mit dem Java-Typ String zurück:



SQLi: Schritt 6. Ermitteln Sie die Abhängigkeiten der runSSSQParams-Parameter von entryPointsParameters, während nur an den Stellen keine Eingabe für die Bereinigung erfolgt


In diesem Schritt verwenden wir die Analysefunktionen. Die folgenden Funktionen werden verwendet, um den Flow-Code zu analysieren:

  • Beeinflusst durch ()
  • InfluencedByAndNotSanitized ()
  • InfluencingOn ()
  • InfluencingOnAndNotSanitized ()
  • NotInfluencedBy ()
  • NotInfluencingOn ()


So ermitteln Sie den Ablauf von runSSSQParams- Anforderungsparametern in Abhängigkeit von den Parametern der übergeordneten Methode entryPointsParameters und schließen Bereinigungstoken aus:

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


Ich bin mir jedoch nicht sicher, ob die * AndNotSanitized- Funktionen etwas Magie bewirken , und es sieht eher so aus, als würde die Methode die bereinigte Menge einfach von ihrem Ergebnis subtrahieren. Das heißt, wenn Sie tun:

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


es stellt sich das gleiche heraus. Obwohl ich vielleicht einfach keine Option gefunden habe, wenn es immer noch Unterschiede gibt.

Das Ergebnis der Abfrage ergibt einen korrekt aufgebauten Flow:



Got Flow mit potenzieller SQL-Injection. Wie aus dem Screenshot hervorgeht, hat Checkmarx 3 Flow zurückgegeben. Der Ablauf im Screenshot ist der kürzeste. Er beginnt und endet in einer Datei und einer Methode. Der nächste Flow geht bereits in die Session-Klasse. Achten Sie auf Quelle / Ziel. Und die letzte ist eine andere Methode in der Session-Klasse. Der Ablauf in der Sitzung sieht folgendermaßen aus:



Um einen Flow auszuwählen, wird die ReduceFlow- Methode (CxList.ReduceFlowType flowType) verwendet , wobei flowType Folgendes sein kann:

  • CxList.ReduceFlowType.ReduceBigFlow - Wählen Sie den kürzesten Flow aus
  • CxList.ReduceFlowType.ReduceSmallFlow - Wählen Sie den längsten Flow aus


SQLi: Letzte Abfrage zum Auffinden der SQL-Injection


 // 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); 


Beispiel 2: Suchen nach unsicheren direkten Objektreferenzen


In dieser Anfrage werden wir nach allen Stellen suchen, an denen mit Objekten gearbeitet wird, ohne den Eigentümer des Objekts zu überprüfen. In diesem Fall können unterschiedliche Namen von HTTP-Parametern für mailboxid verwendet werden (wir gehen davon aus, dass dies Legacy ist), und die Überprüfung selbst kann in verschiedenen Phasen erfolgen: irgendwo direkt am HTTP-Eintrags-API-Punkt, irgendwo vor der Anforderung an die Datenbank und manchmal in Zwischenmethoden.

Plan suchen
  1. Ausnahmen definieren ( exclusionList );
  2. Stellen für Berechtigungsprüfungen identifizieren ( idorSanitizer );
  3. Einstiegspunkte suchen - Orte für die primäre Verarbeitung von HTTP-Anforderungen ( webRemoteMethods );
  4. Nur nach Einstiegspunkt-Token, um den Extraktionsort des HTTP-Parameters mailboxid ( mailboxidInit ) zu ermitteln.
  5. Suchen Sie alle Aufrufe von webRemoteMethods an Middleware-Methoden und Parameter dieser Aufrufe ( middlewareMethods ).
  6. Finden Sie Middleware-Methoden, die von mailboxid ( apiPotentialIDOR ) abhängen.
  7. Suchen Sie alle Stellen, an denen Middleware-Methoden definiert sind ( middlewareDecl ).
  8. Gehen Sie alle apiPotentialIDOR durch und wählen Sie nur die MiddlewareDecl aus, in denen keine Überprüfung des Besitzers des Mailboxid- Objekts erfolgt.


IDOR: Schritt 1. Ausnahmen identifizieren


In diesem Fall schließen Sie alle Token in einer bestimmten Datei aus:

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

WebMethodContext.java enthält eine Implementierung von Methoden wie getMailboxId und getUserId sowie die Zeichenfolge "mailboxid". Da der Name der Token mit dem übereinstimmt, den wir für die Suche nach Sicherheitslücken benötigen, gibt diese Datei falsche Ergebnisse aus.

IDOR: Schritt 2. Suchen Sie die Berechtigungsprüfungen


In der Testanwendung wird mit der validateMailbox () -Methode bestimmt, ob das angeforderte Objekt dem Benutzer gehört:

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

Ergebnis:



IDOR: Schritt 3. Suchen Sie Einstiegspunkte für benutzerdefinierte HTTP-API-Anforderungen


HTTP-Request-Handler sind mit einer speziellen Anmerkung versehen, die das Auffinden erleichtert. In meinem Fall ist dies "WebRemote", die CMxQL-Funktion FindByCustomAttribute () wird verwendet, um nach Anmerkungen zu suchen. Für FindByCustomAttribute () gibt die Suchfunktion des übergeordneten Tokens GetAncOfType () die Methode unter der Anmerkung zurück:

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


Anforderungsergebnis:



IDOR: Schritt 4. Verwenden Sie nur Einstiegspunkt-Token, um die HTTP-Extraktionspositionen für den Parameter mailboxid zu ermitteln


So finden Sie Token, die sich auf die Verarbeitung des HTTP-Parameters mailboxid beziehen:

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

Wir haben 3 Sets mit 3 verschiedenen Zeilen hinzugefügt, weil Der Legende nach kann der Name des HTTP-Parameters in verschiedenen Teilen des Systems unterschiedlich sein.

Die Abfrage findet alle Stellen, an denen mailboxid / mid / boxid als Zeichenfolge geschrieben ist (in doppelten Anführungszeichen). Aber diese Abfrage wird eine Menge Funde zurückgeben, tk. Eine solche Zeichenfolge befindet sich nicht nur an Stellen, an denen HTTP-Parameter extrahiert werden. Wenn wir mit diesem Set weiterarbeiten, werden wir eine große Anzahl falscher Funde erhalten.

Aus diesem Grund suchen wir nur nach Token für Einstiegspunkte ( webRemoteMethods ). Um alle untergeordneten Token zu finden, wird die CMBQL-Funktion GetByAncs () verwendet :

 result = All.GetByAncs(webRemoteMethods); 

Die Anforderung gibt alle Token zurück, die zu als WebRemote gekennzeichneten Methoden gehören . Bereits jetzt können wir die Token der Methoden filtern, bei denen der Eigentümer des Objekts überprüft wird. Daher schreiben wir die vorherige Abfrage neu, um nach untergeordneten Token zu suchen , sodass nur die untergeordneten Token der WebRemote- Methoden ausgewählt werden, bei denen keine Sicherheitsüberprüfung für den Eigentümer des Objekts erfolgt. Verwenden Sie dazu eine Schleife mit der Bedingung:

 //          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); } } 

Jetzt können wir mithilfe der HTTP- Mailbox-ID- Parameter eine genauere Auswahl treffen :

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

Uns interessieren jedoch nicht die Stellen, an denen die HTTP-Parameter abgerufen werden, sondern die Variablen, denen letztendlich die Werte der HTTP-Parameter zugewiesen werden. Da es zuverlässiger ist, Flow genau nach Variablen zu suchen.

Die CMxQL-Funktion FindByInitialization () findet die Stellen der Variableninitialisierung für die angegebenen Token:

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

Ergebnis:



IDOR: Schritt 5. Alle Aufrufe von webRemoteMethods an Middleware-Methoden und -Parameter dieser Aufrufe suchen


Mit Middleware meine ich Code, der tiefer geht als die Verarbeitungsmethoden von HTTP-API-Anforderungen, dh tiefer als die Einstiegspunkte von Benutzeranforderungen. Für den obigen Screenshot sind dies beispielsweise Methoden der User- Klasse, Aufrufe von user.getSettings () und user.getSecureSettings () :

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

Zuerst wählen wir alle Token mit dem Namen user aus, und dann wählen wir mit GetRightmostMember () die Aufruftoken für Middleware aus. GetRightmostMember () in der Kette der Methodenaufrufe gibt das am weitesten rechts stehende zurück. Dann leiten wir die Parameter der gefundenen Methode mit GetParameters () ab .

Ergebnis:



IDOR: Schritt 6. Suchen Sie nach Middleware-Methoden, die von der Mailbox-ID abhängen


Die Durchflussanalyse verwendet die Methoden * InfluencedBy * und * InfluncingOn * . Der Unterschied zwischen ihnen ist durch den Namen klar.

Zum Beispiel:

 All.InfluencedBy(getMailboxHTTPParams) 

Durchläuft die Gruppe Alle und findet alle Token, die von getMailboxHTTPParams abhängen.

Das gleiche kann auf andere Weise geschrieben werden:

 getMailboxHTTPParams.InfluencingOn(All) 


So suchen Sie nach Token, die von mailboxidInit abhängig sind :

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

Ergebnis:



IDOR: Schritt 7. Finden Sie alle Stellen, an denen Sie Middleware-Methoden definieren können


Lassen Sie uns die Definitionen aller Zwischenmethoden finden, die an Stellen verwendet werden können, an denen Benutzeranforderungen verarbeitet werden. Dazu heben wir ihre gemeinsame Eigenschaft hervor. In all diesen Methoden wird beispielsweise ein Request () -Objekt erstellt. Das Erstellen eines Objekts ist vom CMx-Typ ObjectCreateExpr :

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


(All - exclusionList) - Sie können diese Subtraktion von Mengen durchführen und dann die gewünschte CMxQL-Funktion aus dem Ergebnis aufrufen. Requests enthält jetzt alle Token mit dem Namen Request und dem Typ, der der Erstellung des Objekts entspricht.

Als nächstes finden wir mit dem bekannten GetAncOfType () das übergeordnete Token vom Typ MethodDecl .

Ergebnis:



IDOR: Schritt 8. Durchlaufen Sie alle apiPotentialIDOR und wählen Sie nur die MiddlewareDecl aus, in denen keine Überprüfung des Besitzers des Mailboxid-Objekts erfolgt


Im letzten Teil der Anforderung bestimmen wir, welche der Middleware-Methoden direkt von den Einstiegspunktmethoden aufgerufen werden, und überprüfen nicht, wem die Mailbox-ID gehört. Kombinieren Sie dann Flow für eine bequemere Analyse der Ergebnisse.

Neue Funktionen, die wir noch nicht genutzt haben:
GetCxListByPath () - Diese Funktion wird benötigt, um den Flow zu durchlaufen. Wenn sie NICHT verwendet wird, komprimiert CMx den Flow im Code-Element (im ersten Flow-Knoten).
Verketten * () - Eine Reihe von Funktionen, die zum Kombinieren mehrerer Flows zu einem Flow erforderlich sind
FindByParameters () - Findet eine Methode anhand eines bestimmten Parameter-Tokens
GetName () - Gibt eine Zeichenfolge mit dem Tokennamen zurück. Befindet sich mehr als ein Element in CxList, wird die erste zurückgegeben. Die Methode wird nur verwendet, wenn Elemente einer Menge durchlaufen werden.

Der letzte Teil der Anfrage:

 //    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()); } } } 


Ergebnis:



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; 


Fazit


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/de477742/


All Articles