Comment surveiller vos équipements #4 – Les bases de l’AQL
Nous voici déjà dans ce quatrième volet de cette série autour de la collecte de logs. Nous avons dès à présent, mis la collecte sur l'ensemble des équipements de notre/votre SI, qu'ils soient classiques et standardisés mais également les plus à la marge.
Maintenant que nous avons tous ces logs, il va falloir les manipuler et les exploiter pour pouvoir en apprendre un peu plus mais également pouvoir détecter les premiers comportements suspects.
Pour cela nous allons nous intéresser à l'AQL pour Ariel Query Langage, est le langage de requête dans QRadar qui est le pendant de la recherche par filtre classique. Au vue du nom, vous aurez facilement deviné que ce langage est un langage de requêtage d'une base de données. Néanmoins elle est beaucoup plus versatile et comporte notamment les avantages suivants :
- Facilement personnalisable & partageable (ie : automatisation d'investigation)
- Plus rapide que la recherche GUI (via l'utilisation des filtres)
- Permet de faire des recherches complexes mais également de combiner plusieurs assertions au sein d'une seule et même recherche, ce que nous verrons dans un article dédié pour parler de l'AQL de manière plus poussée
- Plusieurs possibilités de recherche qui sont exclusives à l'AQL (ie : Session By que nous verrons dans un article dédié pour parler de l'AQL de manière plus poussée)
Avant de commencer à expliquer les bases, revenons rapidement sur du purement théorique. Les requêtes que vous faites en AQL, qui est très similaire au SQL, vont allées taper directement dans la base de données. Cette base de données et l'écriture des événements sur cette dernière se situe juste après la corrélation ou CRE pour Custom Rule Engine (pour vérifier si des règles ont sonné), et donc après également le parsing et le mapping des événements.
Nous avons donc des événements parfaitement prêt à être utilisés avec toutes les informations nécessaires. Dans la suite de l'article, vous allez voir comment utiliser les bases de ce langage pour pouvoir faire vos premières recherches. Nous verrons dans un prochain article comment complexifier les requêtes pour mettre sur le papier quasiment toutes vos envies de recherches.
2. Les Bases
Dans cette première partie, nous allons voir ce qui fait l'essence des requêtes.
a. Où faire de l'AQL
Pour effectuer vos premières recherches AQL, il faut se rendre dans l'onglet "Log Activity" puis choisir "Advanced Search" comme ci-dessous :
Pour valider la recherche, il faudra cliquer sur "Search" ou taper "Entrée".
b. La structure
La structure de base des recherches AQL est la suivante :
select /*les colonnes que je veux*/ from /*la table que je veux*/
where
/*les filtres et/ou fonction que je veux*/
En complément de cette rapide description, il faut noter les points suivants :
- Vous pouvez écrire en minuscule/majuscule, il n'y a pas d'importance
- Les retours à la ligne sont facultatifs (vous pouvez donc faire vos meilleurs one liner ou uniligne d'après Google)
- Dans la partie des filtres il y a également la partie temporelle comme par exemple l'intervalle de temps que je sélectionne dans ma recherche
- Pour les événements vous choisirez la table « events »
c. Les premiers filtres
Maintenant que vous connaissez la structure, nous allons voire les premiers filtres qui vont vous permettre de pouvoir réaliser vos premières requêtes. Mais avant, faisons un point rapide sur les différents types de données que vous allez rencontrer :
- Les ID : logsourceid, collectorid, processorid…
- Les chaînes de caractères : username, sourceip
- Les valeurs décimales : sourceport, destinationport
Pour chacun des types, les filtres s'appliquent différemment, par exemple pour les "ID" et les valeurs décimales, les opérateurs mathématiques sont de mises :
/*si je souhaite avoir que les événements venant du collecteur ayant pour id 1*/
collectorid = 1
/*si je souhaite avoir que les événements ayant pour port source 1000*/
sourceport = 1000
D'un autre côté les chaînes de caractère, il est également possible de faire appel aux opérateurs mathématiques pour filtrer vos recherches mais il est aussi possible d'utiliser des fonctions plus poussées comme les expressions régulières. Voici quelques exemples simples :
/*si je souhaite avoir que les événements de l'utilisateur toto*/
username = 'toto'
/*si je souhaite avoir que les événements de l'utilisateur toto mais aussi de l'utilisateur toto-perso*/
username ilike 'toto%'
Enfin, il est également possible de concaténer tous ces filtres en utilisant des opérateurs logiques mathématiques comme :
- and
- or
- and not
- or not
Ainsi, si l'on souhaite effectuer la requête suivante :
Avoir tous les événements d'une log source spécifique (ID=562) et seulement ceux dont le champ utilisateur contient "DWM"
Vous pouvez faire :
select *
from events
where
logsourceid = 562
and username ilike '%dwm%'
Voilà, vous maîtrisez la base des premiers filtres, pour creuser le sujet je vous conseille de passer en revue tout ce que vous pouvez faire avec la documentation IBM disponible ici : https://www.ibm.com/docs/en/qsip/7.4?topic=structure-where-clause
Il est également très important de tester encore et toujours pour se heurter à des problèmes, les résoudre et maîtriser encore plus l'AQL.
d. Les premières fonctions
Vous avez vos premiers filtres de prêt mais ils restent assez basiques, je vais vous apporter des outils pour amener vos requêtes à un niveau supérieur. En effet, il est possible d'utiliser des fonctions de récupération de données comme :
- les données de géolocalisation d'une adresse IP
- le nom d'usage de plusieurs équipements via leur ID
- les données présentes dans les référentiels que vous avez sur votre QRadar
Je vais vous présenter plusieurs exemples juste après, ensuite comme pour la partie précédente je vous invite à lire toutes fonctions disponibles sur la documentation (https://www.ibm.com/docs/en/qsip/7.4?topic=language-aql-data-retrieval-functions).
Exemple #1
Avoir tous les événements d'une log source spécifique (ID=171) et seulement ceux dont l'IP source est physiquement basée aux USA
select *
from events
where
logsourceid = 171
and GEO::LOOKUP(sourceip, 'physical_country') ilike '%"name":"United States"%'
Exemple #2
Avoir tous les événements d'une log source spécifique (ID=562) et seulement ceux qui contiennent une des chaînes de caractère suivante :
- "start"
- "stop"
- "démarrer"
- "arrêt"
Cela permet de pouvoir détecter des redémarrages/arrêts de service de machine…etc
select *
from events
where
logsourceid = 562
and (utf8(payload) ilike '%start%' or utf8(payload) ilike '%stop%' or utf8(payload) ilike '%démarrer%' or utf8(payload) ilike '%arrêt%')
e. Un peu de temporalité
Avant d'avoir une requête complète, nous allons rajouter un peu de temporalité pour avoir un filtrage de temps. Il existe plusieurs moyens, dont le plus intuitif est le suivant :
select *
from events
where
/*
mes filtres
*/
last 10 minutes
L'utilisation du mot clé last suivi d'un nombre entier suivi du mot clé temporel parmi :
- seconds
- minutes
- days
Cela permet d'avoir les résultats de l'instant T à l'instant T moins X secondes, minutes ou jours en fonction de votre choix.
D'un autre côté il est possible de préciser les deux extremums de l'intervalle en faisant comme ci-dessous :
select *
from events
where
/*
mes filtres
*/
start 'yyyy-MM-dd HH:mm:ss'
stop 'yyyy-MM-dd HH:mm:ss'
Dans l'exemple ci-dessus j'utilise le formalisme yyyy-MM-dd HH:mm:ss mais il en existe plusieurs que vous retrouverez dans la documentation suivante : https://www.ibm.com/docs/en/qsip/7.4?topic=language-time-criteria-in-aql-queries
Enfin, il existe la possibilité d'utiliser la fonction PARSEDATETIME() qui permet de transformer une assertion en filtre temporel, comme par exemple :
select *
from events
where
/*
mes filtres
*/
start PARSEDATETIME('2 hour ago')
stop PARSEDATETIME('1 hour ago')
Dans la requête ci-dessus, le filtrage temporel permet d'avoir les événements sur l'intervalle [T-1h;T-2h] avec T le temps à laquelle la requête est lancée.
3. Exemples d'utilisation
Dans cette dernière partie nous allons voir quelques exemples qui vous permettrons peut-être de vous lancer dans vos propres requêtes plus facilement. Pour chacun j'expliquerai rapidement dans une assertion le but de la requête.
Dans les exemples, je vais rajouter quelques commentaires entre les balises /* commentaire */, vous pouvez les garder dans vos recherches car QRadar va tout simplement les ignorer.
a. Débug d'une log source
Le but de la recherche est de trouver l'origine d'un dysfonctionnement d'une log source. Pour ce faire nous allons filtrer par log source via l'ID de cette dernière que l'on peut récupérer au travers de l'application "Log Source Management". Nous allons également rechercher des mots clés dans le log entier à la recherche d'arrêt de service par exemple.
select
DATEFORMAT(starttime,'dd-MM-yyyy hh:mm:ss') as "Date",
QIDNAME(qid) as "Event Name",
UTF8(payload) as "Log"
from events
where
/* changer avec l'ID de la log source souhaitée */
logsourceid = <LOGSOURCEID>
/* rajouter des mots clés au besoin */
and (UTF8(payload) ilike '%restart%' or UTF8(payload) ilike '%stop%' or UTF8(payload) ilike '%shutdown%')
/* filtrage des logs de manière chronologique */
order by starttime desc
/* changer le début de l'intervalle en fonction des dernières heures d'émission de logs */
start '<yyyy-MM-dd HH:mm>'
stop '<yyyy-MM-dd HH:mm>'
b. Recherche de pics de logs
Le but de la recherche est d'identifier les pics de logs de manière précise. Pour cela, nous allons utiliser un petit subterfuge qui permet de "grouper" des événements d'une même minute (ou heure ou jour… en fonction des besoins). C'est possible grâce à la fonction DATEFORMAT() , qui, lorsqu'on lui passe une constante comme paramètre, va mettre cette constante à tous les événements. Ainsi on aura la transposition suivante :
DATEFORMAT(starttime,'dd-MM-yyyy hh:mm:ss') DATEFORMAT(starttime,'dd-MM-yyyy hh:mm:00′) 01-01-2022 10:10:01 01-01-2022 10:10:00 01-01-2022 10:10:45 01-01-2022 10:10:00 01-01-2022 10:11:59 01-01-2022 10:11:00 Ensuite, en groupant comme il le faut on obtient un tableau minute par minute de la log source qui a le plus communiquée avec le nombre de logs associé.
select
/* Définir le format de la date en forçant les secondes à 0, comme ceci tous les logs d'une même minute vont avoir la même "Date" */
DATEFORMAT(starttime,'dd-MM-yyyy hh:mm:00') as "Date",
/* Récupérer la log source qui a la valeur maximale en terme d'occurrence */
MAX(LOGSOURCENAME(logsourceid)) as "Top log source",
/* Récupérer le nombre de logs au travers de la fonction COUNT */
COUNT(*) as "Somme Logs"
from events
where
/* Ne pas prendre les log sources internes à QRadar (Notification par exemple) */
(logsourceid > 69 or logsourceid < 62)
group by "Date"
order by "Somme Logs" DESC
last 30 minutes
5. Conclusion
Ça y est vous avez maintenant les bases de l'AQL, vous allez pouvoir approfondir vos investigations et récupérer toutes les informations que vous souhaitez sur les log sources mais aussi sur vos équipements QRadar.
N'hésitez pas à commenter avec vos meilleures recherches AQL ou vos questions 😊
6. Bibliographie
- Documentation complète sur l'AQL sur le site IBM (naviguez dans les différents onglets pour retrouver la partie qui vous intéresse) : https://www.ibm.com/docs/en/qsip/7.4?topic=aql-ariel-query-language