
Ein CodeQL-Workshop zu CVE-2021-21380
In diesem Workshop werden wir syntaktische und semantische Schlussfolgerungen verwenden, um eine SQL-Injection in der Rating-Komponente der XWiki-Plattform zu finden, die durch CVE-2021-21380 dokumentiert ist.
Bitte arbeiten Sie diesen Abschnitt, wenn möglich, vor dem Workshop durch.
Installieren Sie Visual Studio Code.
Installieren Sie die CodeQL-Erweiterung für Visual Studio Code.
Sie müssen die CodeQL-CLI nicht installieren: Die Erweiterung übernimmt dies für Sie.
Klonen Sie dieses Repository:
git clone https://github.com/advanced-security/codeql-workshop-cve-2021-21380.git
xwiki-platform-CVE-2021-21380.zip in der Explorer-Ansicht und wählen Sie den Befehl CodeQL: Set Current Database.Cmd/Ctrl+Shift+P) nach dem Befehl CodeQL: Install Pack Dependencies und führen Sie ihn aus.github in das Feld ein, um die Liste zu filtern.cve-2021-21380-exercises, cve-2021-21380-exercises-tests, cve-2021-21380-solutions und cve-2021-21380-solutions-tests.Der Workshop ist in mehrere Übungen unterteilt, die die QL-Sprachunterstützung für Java vorstellen, und endet mit einer abschließenden Abfrage, um die bekannte SQL-Injection zu finden. In diesen Übungen lernen Sie:
In diesem Workshop suchen wir nach bekannten SQL-Injection-Schwachstellen in der Rating-API-Komponente der XWiki Platform. Solche Schwachstellen können in Anwendungen auftreten, wenn Informationen, die von einem externen Benutzer kontrolliert werden, in Anwendungscode gelangen, der auf unsichere Weise eine SQL-Abfrage erstellt und ausführt.
Die in diesem Workshop behandelte bekannte SQL-Injection wird in GHSA-79rg-7mv3-jrr5 in der GitHub Advisory Database überprüft. Um die SQL-Injection und mögliche Varianten zu finden, gehen wir die folgenden Teilprobleme an:
In den ersten Übungen werden wir mithilfe des abstrakten Syntaxbaums (AST) über syntaktische Informationen schlussfolgern, um Folgendes zu identifizieren:
Finden Sie alle Methoden mit dem Namen getAverageRating und ihrem deklarierenden Typ im Programm, indem Sie die Abfrage exercise1.ql vervollständigen.
java-Modul stellt eine Klasse Method bereit, um über Methoden in einem Programm zu schlussfolgern.Method stellt die Member-Prädikate getName und hasName bereit, um über den Namen einer Methode zu schlussfolgern.Method stellt das Member getDeclaringType bereit, um über den Typ zu schlussfolgern, der die Methode deklariert.Eine Lösung finden Sie in der Abfrage exercise1.ql
Eine Lösung zu Übung 1 gibt eine Liste von Methoden zurück. Einige davon sind in einem Interface namens RatingsManager definiert, andere in den Klassen AbstractRatingsManager und RatingsScriptService.
Aus den von der Abfrage zurückgegebenen Informationen und der XWiki-Komponentendokumentation können wir ableiten:
Role annotiert ist, und einer Implementierung, die mit Component annotiert ist.ScriptService erweitert, wird Wiki-Seiten über Scripting zugänglich gemacht.Finden Sie alle Klassen, die mit der Annotation Component annotiert sind, indem Sie die Abfrage exercise2.ql vervollständigen.
Beachten Sie, dass der voll qualifizierte Name des Typs der Annotation org.xwiki.component.annotation.Component ist.
this im /charakteristischen Prädikat/ abgerufen werden.Class stellt eine Methode getAnAnnotation bereit, um zugehörige Annotationen zu erhalten.Annotation stellt das Member-Prädikat getType bereit, um über ihren Typ zu schlussfolgern.Type stellt die Member-Prädikate getName und hasName bereit, um über den Namen eines Typs zu schlussfolgern.RefType, die Klassen und Interfaces repräsentiert, stellt die Member-Prädikate getQualifiedName und hasQualifiedName bereit, um über den voll qualifizierten Namen des RefType zu schlussfolgern.Eine Lösung finden Sie in der Abfrage exercise2.ql
Finden Sie alle Komponenten, die das ScriptService-Interface implementieren, indem Sie die Abfrage excercise3.ql vervollständigen.
Class stellt das Member-Prädikat getASuperType bereit, um über die Supertypen einer Klasse zu schlussfolgern, also Typen, die sie extends oder implements.Eine Lösung finden Sie in der Abfrage exercise3.ql
An diesem Punkt haben wir syntaktisch die Methoden identifiziert, die von einem Benutzer aufgerufen werden können und deren Parameter wir später im Workshop als Quellen nicht vertrauenswürdiger Daten betrachten werden.
In der nächsten Übung werden wir mögliche Senken untersuchen und identifizieren. Aus den Ergebnissen von Übung 1 können wir ableiten, dass eine der Implementierungen die Methode getAverageRatingFromQuery aufruft. Mit einer ähnlichen Abfrage können wir die deklarierenden Typen von getAverageRatingFromQuery finden, wodurch wir feststellen können, dass die Implementierung in der Klasse AbstractRatingsManager eine SQL-Anweisung erstellt, die an die Methode search übergeben wird.
Die Methode search ist in einer Abhängigkeit implementiert, daher ist ihre Implementierung nicht verfügbar. In den nächsten Übungen werden wir die verfügbaren Typinformationen verwenden, um diesen search-Methodenaufruf und seinen deklarierenden Typ zu identifizieren.
Finden Sie alle Methodenaufrufe an die Methode search und identifizieren Sie ihren deklarierenden Typ, indem Sie die Abfrage exercise4.ql vervollständigen.
MethodCall bietet uns die Möglichkeit, über Methodenaufrufe zu schlussfolgern.MethodAcccess stellt das Member-Prädikat getMethod bereit, um über das Ziel eines Methodenaufrufs zu schlussfolgern.Eine Lösung finden Sie in der Abfrage exercise4.ql
Finden Sie alle Methodenaufrufe an Methoden, die vom Interface XWikiStorageInterface deklariert werden (mit dem qualifizierten Namen com.xpn.xwiki.store.XWikiStoreInterface), indem Sie die Abfrage exercise5.ql vervollständigen.
Das Schlüsselwort instanceof kann verwendet werden, um auszudrücken, dass ein Wert zur Menge der Werte gehört, die durch einen Typ repräsentiert werden.
Um beispielsweise alle Aufrufe von Interface-Methoden zu identifizieren:
import java
from MethodCall ma
where ma.getMethod.getDeclaringType() instanceof Interface
select ma
Eine Lösung finden Sie in der Abfrage exercise5.ql
An diesem Punkt haben wir mögliche Quellen und Senken mithilfe von QL syntaktisch beschrieben. Um festzustellen, ob Informationen zwischen diesen Punkten im Programm fließen, werden wir das Programm mithilfe von Taint-Tracking semantisch analysieren. Die Standardbibliotheken der von uns unterstützten Sprachen bieten zwei Datenflussmechanismen:
DataFlow unterstützt wert-erhaltenden Informationsfluss.TaintTracking unterstützt Informationsfluss, selbst wenn Werte verändert werden.Letzteres ist von Interesse, da bei Injektionsschwachstellen wie SQL-Injection nicht vertrauenswürdige Daten häufig Teil einer größeren Anweisung werden, die ausgeführt wird.
In diesem Workshop werden wir eine bestehende Taint-Tracking-Konfiguration für SQL-Injection wiederverwenden und sie mit unseren modellierten Quellen und Senken erweitern, um die SQL-Injection zu finden. Um zu verstehen, wie wir die Konfiguration erweitern können, beginnen wir damit, uns die Definition der Konfiguration unten anzusehen.
class QueryInjectionFlowConfig extends TaintTracking::Configuration {
QueryInjectionFlowConfig() { this = "SqlInjectionLib::QueryInjectionFlowConfig" }
override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof QueryInjectionSink }
override predicate isSanitizer(DataFlow::Node node) {
node.getType() instanceof PrimitiveType or
node.getType() instanceof BoxedType or
node.getType() instanceof NumberType
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
any(AdditionalQueryInjectionTaintStep s).step(node1, node2)
}
}
Sowohl das isSource- als auch das isSink-Prädikat, die von der Konfiguration verwendet werden, um diese Programmpunkte zu identifizieren, verwenden das Schlüsselwort instanceof, das wir bereits gesehen haben. Sowohl die Klasse RemoteFlowSource als auch die Klasse QueryInjectionSink sind abstract-Klassen.
Dies ist ein häufiges Muster, das Ihnen in den Standardbibliotheken begegnen wird, und dieses Muster ermöglicht es uns, die Menge der Werte zu erweitern, die durch die Klassen RemoteFlowSource und QueryInjectionSink repräsentiert werden.
Eine QL-class, die eine abstract class erweitert, verfeinert nicht die Menge der Werte, die durch die Oberklasse repräsentiert wird, sondern fügt der Oberklasse die Werte hinzu, die durch die Unterklasse repräsentiert werden.
In den nächsten Übungen werden wir diese Unterklassen implementieren und unsere endgültige Abfrage konstruieren.
Erweitern Sie die Wertemenge von RemoteFlowSource um die Parameter der öffentlichen Methoden der in Übung 3 identifizierten Komponentenklassen, indem Sie die Abfrage exercise6.ql vervollständigen.
Das Schlüsselwort instanceof kann verwendet werden, um auszudrücken, dass ein Wert zur Menge der Werte gehört, die durch einen Typ repräsentiert werden.
Um beispielsweise alle Aufrufe von Interface-Methoden zu identifizieren:
import java
from MethodCall ma
where ma.getMethod.getDeclaringType() instanceof Interface
select ma
Eine Lösung finden Sie in der Abfrage exercise6.ql
Erweitern Sie die Wertemenge von QueryInjectionSink um die Argumente von Aufrufen an die Methoden des in Übung 5 identifizierten Storage-Interfaces, indem Sie die Abfrage exercise7.ql vervollständigen.
Die exists-Formel ermöglicht die Einführung temporärer Variablen, über die im Gültigkeitsbereich von exists Schlussfolgerungen angestellt werden können.
Das folgende Beispiel verwendet den exists-Ausdruck, um die Menge der Methoden auf die Methoden zu reduzieren, die aufgerufen werden.
from Method m
where m.hasName("foo") and exists(MethodCall ma | ma.getMethod() = m)
select m
Die Klasse MethodCall stellt das Member-Prädikat getQualifer bereit, um über den Qualifier des Methodenzugriffs zu schlussfolgern.
Eine Lösung finden Sie in der Abfrage exercise7.ql
Kombinieren Sie Ihre Lösungen aus der vorherigen Übung zu einer endgültigen Lösung, indem Sie die Abfrage exercise8.ql vervollständigen.
Eine Lösung finden Sie in der Abfrage exercise8.ql
XWikiStoreInterface beschränkt. Erweitern Sie die Senke um direkte Verwendungen von Implementierungen des Interfaces. Die Klasse Class stellt das Member-Prädikat extendsOrImplements und die Klasse Method das Member-Prädikat overridesOrInstantiates bereit, die hilfreich sein könnten.