
Un workshop CodeQL che copre CVE-2021-21380
In questo workshop useremo il ragionamento sintattico e semantico per trovare una SQL injection nel componente di valutazione della piattaforma XWiki documentata da CVE-2021-21380.
Ti preghiamo di completare questa sezione prima del workshop, se possibile.
Installa Visual Studio Code.
Installa l'estensione CodeQL per Visual Studio Code.
Non è necessario installare il CodeQL CLI: l'estensione lo gestirà per te.
Clona questo repository:
git clone https://github.com/advanced-security/codeql-workshop-cve-2021-21380.git
xwiki-platform-CVE-2021-21380.zip nella vista Explorer e seleziona il comando CodeQL: Set Current Database.Cmd/Ctrl+Shift+P), cerca ed esegui il comando CodeQL: Install Pack Dependencies.github nella casella per filtrare l'elenco.cve-2021-21380-exercises, cve-2021-21380-exercises-tests, cve-2021-21380-solutions e cve-2021-21380-solutions-tests.Il workshop è suddiviso in diversi esercizi che introducono il supporto del linguaggio QL per Java e termina con una query finale per trovare la SQL injection nota. In questi esercizi imparerai:
In questo workshop cercheremo delle vulnerabilità di SQL injection note nel componente API di valutazione della XWiki Platform. Tali vulnerabilità possono verificarsi nelle applicazioni quando informazioni controllate da un utente esterno raggiungono codice applicativo che costruisce in modo non sicuro una query SQL e la esegue.
La SQL injection nota discussa in questo workshop è recensita in GHSA-79rg-7mv3-jrr5 nel GitHub Advisory Database. Per trovare la SQL injection, e le possibili varianti, ci occuperemo dei seguenti sottoproblemi:
Nei primi esercizi ragioneremo sulle informazioni sintattiche, usando l'Abstract Syntax Tree (AST), per identificare:
Trova tutti i metodi con nome getAverageRating e il loro tipo dichiarante nel programma completando la query exercise1.ql
java fornisce una classe Method per ragionare sui metodi di un programma.Method fornisce i predicati membro getName e hasName per ragionare sul nome di un metodo.Method fornisce il membro getDeclaringType per ragionare sul tipo che dichiara il metodo.Una soluzione si trova nella query exercise1.ql
Una soluzione all'esercizio 1 restituisce un elenco di metodi. Alcuni di essi sono definiti in un'interfaccia chiamata RatingsManager, altri sono definiti nelle classi AbstractRatingsManager e RatingsScriptService.
Dalle informazioni restituite dalla query e dalla documentazione dei componenti XWiki possiamo dedurre che:
Role, e da un'implementazione annotata con Component.ScriptService viene reso accessibile alle pagine wiki tramite scripting.Trova tutte le classi annotate con l'annotazione Component completando la query exercise2.ql.
Nota che il nome completo del tipo dell'annotazione è org.xwiki.component.annotation.Component.
this nel /predicato caratteristico/.Class fornisce un metodo getAnAnnotation per ottenere le annotazioni associate.Annotation fornisce il predicato membro getType per ragionare sul suo tipo.Type fornisce i predicati membro getName e hasName per ragionare sul nome di un tipo.RefType, che rappresenta classi e interfacce, fornisce i predicati membro getQualifiedName e hasQualifiedName per ragionare sul nome completo del reftype.Una soluzione si trova nella query exercise2.ql
Trova tutti i componenti che implementano l'interfaccia ScriptService completando la query excercise3.ql
Class fornisce il predicato membro getASuperType per ragionare sui super tipi di una classe, cioè i tipi che essa extends o implements.Una soluzione si trova nella query exercise3.ql
A questo punto abbiamo identificato sintatticamente i metodi che possono essere chiamati da un utente e i cui parametri considereremo fonti di dati non fidati più avanti nel workshop.
Nell'esercizio successivo andremo a indagare e identificare possibili sink. Dai risultati dell'esercizio 1 possiamo dedurre che una delle implementazioni chiama il metodo getAverageRatingFromQuery. Usando una query simile possiamo trovare i tipi dichiaranti di getAverageRatingFromQuery, il che ci consente di stabilire che l'implementazione nella classe AbstractRatingsManager costruisce un'istruzione SQL passata al metodo search.
Il metodo search è implementato in una dipendenza e quindi la sua implementazione non è disponibile. Nei prossimi esercizi useremo le informazioni sui tipi disponibili per identificare questa chiamata al metodo search e il suo tipo dichiarante.
Trova tutte le chiamate al metodo search e identifica il suo tipo dichiarante completando la query exercise4.ql
MethodCall ci fornisce i mezzi per ragionare sulle chiamate di metodo.MethodAcccess fornisce il predicato membro getMethod per ragionare sul target di una chiamata di metodo.Una soluzione si trova nella query exercise4.ql
Trova tutte le chiamate a metodi dichiarati dall'interfaccia XWikiStorageInterface (con nome completo com.xpn.xwiki.store.XWikiStoreInterface) completando la query exercise5.ql
La parola chiave instanceof può essere usata per affermare che un valore appartiene all'insieme di valori rappresentati da un tipo.
Ad esempio, per identificare tutte le chiamate ai metodi di interfaccia:
import java
from MethodCall ma
where ma.getMethod.getDeclaringType() instanceof Interface
select ma
Una soluzione si trova nella query exercise5.ql
A questo punto abbiamo descritto sintatticamente possibili sorgenti e sink usando QL. Per determinare se le informazioni fluiscono tra questi punti nel programma, analizzeremo semanticamente il programma usando il taint tracking. Le librerie standard dei linguaggi che supportiamo forniscono due meccanismi di flusso dati:
DataFlow supporta il flusso di informazioni che preserva il valore.TaintTracking supporta il flusso di informazioni anche se i valori vengono modificati.Quest'ultimo è interessante, perché nelle vulnerabilità di injection come la SQL injection è comune che dati non fidati diventino parte di un'istruzione più ampia su cui viene eseguita un'azione.
In questo workshop riutilizzeremo una configurazione esistente di taint tracking per SQL injection e la estenderemo con le nostre sorgenti e i nostri sink modellati per trovare la SQL injection. Per capire come possiamo estendere la configurazione, iniziamo osservando la definizione della configurazione qui sotto.
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)
}
}
Sia il predicato isSource che il predicato isSink usati dalla configurazione per identificare quelle posizioni del programma usano la parola chiave instanceof che abbiamo visto prima. Sia la classe RemoteFlowSource che la classe QueryInjectionSink sono classi abstract.
Questo è un pattern comune che si incontra nelle librerie standard e questo pattern ci consente di estendere l'insieme di valori rappresentati dalle classi RemoteFlowSource e QueryInjectionSink.
Una class QL che estende una abstract class non perfeziona l'insieme di valori rappresentato dalla superclasse, ma aggiunge i valori rappresentati dalla sottoclasse alla superclasse.
Nei prossimi esercizi implementeremo queste sottoclassi e costruiremo la nostra query finale.
Estendi l'insieme di valori di RemoteFlowSource con i parametri dei metodi pubblici delle classi dei componenti identificate nell'esercizio 3 completando la query exercise6.ql.
La parola chiave instanceof può essere usata per affermare che un valore appartiene all'insieme di valori rappresentati da un tipo.
Ad esempio, per identificare tutte le chiamate ai metodi di interfaccia:
import java
from MethodCall ma
where ma.getMethod.getDeclaringType() instanceof Interface
select ma
Una soluzione si trova nella query exercise6.ql
Estendi l'insieme di valori di QueryInjectionSink con gli argomenti delle chiamate ai metodi dell'interfaccia di storage identificata nell'esercizio 5 completando la query exercise7.ql
La formula exists consente di introdurre una variabile temporanea di cui è possibile ragionare nello scope dell'exists.
Il seguente esempio usa l'espressione exists per ridurre l'insieme di metodi ai metodi che vengono chiamati.
from Method m
where m.hasName("foo") and exists(MethodCall ma | ma.getMethod() = m)
select m
La classe MethodCall fornisce il predicato membro getQualifer per ragionare sul qualificatore dell'accesso al metodo.
Una soluzione si trova nella query exercise7.ql
Combina le tue soluzioni degli esercizi precedenti in una soluzione finale completando la query exercise8.ql.
Una soluzione si trova nella query exercise8.ql
XWikiStoreInterface. Espandi il sink per includere gli usi diretti delle implementazioni dell'interfaccia. La classe Class fornisce il predicato membro extendsOrImplements e la classe Method fornisce il predicato membro overridesOrInstantiates che potrebbero essere utili.