Veille sécuritaire avec flux RSS (part 1)

Il existe sur Internet, de nombreux sites de veille sécuritaire permettant de vous maintenir au courant des dernières vulnérabilités découvertes. Si vous utilisez une application open-source (forum, blog, cms …), des visites régulières sur ces sites ou abonnement aux flux RSS, vous permettent de maintenir à jour votre application et minimiser les risques de piratages.
Mais, que vous utilisiez une application web open-source (ou non) à jour, cela n’empêche pas les tentatives d’attaques, c’est pourquoi je vous propose ici un petit script python pour effectuer vous même votre veille sécuritaire.
Cet article est la 1ère partie d’une série de 3 :
- Part 1: Configuration des logs apache, création de la BDD puis présentation et configuration du script de traitement.
- Part 2: Script de génération du flux RSS avec présentation détaillées des informations dans le flux.
- Part 3: Amélioration du script avec des expressions régulières plus poussés, rajout d’options …
Il est intéressant de noter que la Partie 1 est la plus importante, les 2 autres parties pouvant être réalisées sans attendre la suite de l’article si vous savez programmer.
Logs apache, BDD & script de traitement
Configuration des logs apache
Ce script de veille se base sur les fichiers de logs apache, il faut donc vous assurer dans un premier temps que les directives de logs sont bien renseignées au niveau de votre configuration.
L’article ne portant pas sur la configuration d’apache, je ne vais pas m’étendre dessus, voici donc les lignes nécessaires à la génération des logs.
LogFormat %V %{canonical}p %h %{X-Forwarded-For}i %{PHPSESSID}C %t \"%r\" %gt;s %b \"%{Referer}i\" \"%{User-Agent}i\" ErrorLog /var/log/apache/my_error_log
Ceci est un exemple de format de log, vous pouvez le modifier sans problème :
- %V: Nom du serveur
- %{canonical}p: port
- %h: IP du visiteur
- %{X-Forwareded-for}i: IP du proxy
- %{PHPSESSID}C: ID de session (pratique pour retracer le chemin d’un pirate potentiel)
- %t: Date
- %r: Requête
- %>s: Status de la requête
- %b: Taille de la requête
- %{Referer}i: Referer (lien précédent)
- %{User-Agent}i: Informations sur le navigateur du visiteur
Pour plus d’infos, je vous invite à consulter la documentation officielle concernant les directives logs d’apache.
Une fois votre fichier modifié, n’oubliez pas de le recharger. Un simple reload doit normalement suffire.
/etc/init.d/apache2 reload
Création de la BDD MySQL
Les informations traitées par le script seront enregistrées en BDD pour pouvoir garder une trace, voici le code SQL pour la création des tables concernées.
-- -- Création de la table des attaques potentielles recensées -- CREATE TABLE `veille_vuln` ( `id_vuln` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, `ip` VARCHAR(15), `url` text, `log` text, `temps` datetime, `id_type` tinyint(3) UNSIGNED, PRIMARY KEY (`id_vuln`), UNIQUE KEY (`ip`,`url`(150),`log`(150),`temps`,`id_type`) ) ENGINE=MyISAM DEFAULT CHARSET utf8; -- -- Création de la table des types d'attaques surveillées -- CREATE TABLE `veille_vuln_type` ( `id_type` tinyint(3) UNSIGNED NOT NULL DEFAULT '0', `nom` VARCHAR(45), `link` VARCHAR(250), PRIMARY KEY (`id_type`) ) ENGINE=MyISAM DEFAULT CHARSET utf8; -- -- Insertion des types de vulnérabilités surveillées -- INSERT INTO `veille_vuln_type` (`id_type`, `nom`, `link`) VALUES ('1', 'XSS', 'http://fr.wikipedia.org/wiki/Cross_site_scripting'), ('2', 'Unicode', ''), ('3', 'Include', ''), ('4', 'SQL Injection', 'http://fr.wikipedia.org/wiki/Injection_SQL'), ('5', 'Mot sensible', '');
Script veille-secu
Une fois les logs apache configurés et la BDD créée, nous allons nous attaquer au script de traitement.
En début de script, appel des différentes librairies nécessaires.
#!/usr/bin/python # Importation des librairies necessaires import re, cgi, time, MySQLdb, sys from getopt import getopt,GetoptError
Ensuite, la partie à configurer pour :
- Effectuer la connexion à la BDD
- Indiquer le fichier pointer
- Gérer vos expressions régulières.
Le fichier pointer va vous permettre de conserver la dernière ligne de logs traitée par le script afin de ne pas repasser sur tout le fichier et utiliser des ressources mémoires inutilement.
Juste après ces infos, deux bloc d’expressions régulières qui permettront de traiter la ligne de log pour la découper puis de détecter une éventuelle tentative d’attaque.
#---------------------------------# # PARTIE A CONFIGURER #---------------------------------# # Declaration des variables generales (chemin absolu) file_pointer = '/path/to/your/pointer' # Declaration des variables de connexion a la BDD MySQL bdd_host = '127.0.0.1' bdd_user = 'mon_user' bdd_pass = 'mon_pass' bdd_db = 'ma_bdd' # Declaration des regexp de traitement des logs pattern_ip = re.compile('[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') pattern_date = re.compile('[0-9]{2}\/...\/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2}') pattern_url = re.compile('(GET|POST|PUT|DELETE|HEAD|PROPFIND|LOCK|OPTIONS)\ [^ ]*\ ') # Declaration des regexp de detection d'attaques potentielles p_faille_xss = re.compile('((\%3C)|<)[^\n]+((\%3E)|>)', re.IGNORECASE) p_faille_unicode = re.compile('\.exe|\/syntaxe\/winnt\/system32\/', re.IGNORECASE) p_faille_inc = re.compile('(http|ftp):\/\/|\/etc\/(passwd|shadow)|\/(sbin|bin)\/|\.\.\/', re.IGNORECASE) p_faille_sql = re.compile('(\ |%20)(union|or)(\ |%20)', re.IGNORECASE) p_faille_word = re.compile('(root|htaccess|passwd|\.log|\.conf)', re.IGNORECASE)
La suite du fichier concerne le traitement des options et des arguments qu’il est possible de passer au script, ici une seule option « –help » et un seul argument « le fichier ». Nous verrons dans la partie 3 des cas beaucoup plus poussés pour faire de ce script un mini IDS et limiter au maximum les faux-positifs.
#-----------------------------------------------------# # NE PAS MODIFIER CETTE PARTIE #-----------------------------------------------------# # Declaration des options et arguments optionmap = [ ["-h", "--help", "Affiche l'aide"], ["-f", "--logfile", "Specifie le fichier de log a traiter (chemin absolu)"], ] # Traitement des options et arguments args = [] params = [] try: args, params = getopt(sys.argv[1:], "".join([o[0][1] for o in optionmap]), \ [x[2:] for x in reduce(lambda x,y: x+y, [z[1:-1] for z in optionmap])]) args = [a for a,b in args] log = 0 mode = None for option in ["--help", "-h"]: if option in args: args.remove(option) mode = "help" for option in ["--logfile", "-f"]: if option in args: args.remove(option) log = params.pop(0) # Traitement des errreurs except GetoptError, e: sys.stderr.write("option invalide: ") sys.stderr.write(str(e)+"\n") mode = "help" # Affichage de l'aide si le mode "help" est defini ou si le fichier de log est equivalent a 0 if mode == "help" or log == 0: sys.stderr.write("Syntax: veille-secu \n") for m in optionmap: sys.stderr.write(m[0] + "\t" + m[1] + " \t: " + m[-1] + "\n") for o in m[2:-1]: sys.stderr.write("\t" + o + "\n") sys.exit(1)
Connexion à la BDD avec les informations renseignées en début de fichier.
# Connexion a la BDD cnx = MySQLdb.connect( host = bdd_host, user = bdd_user, passwd = bdd_pass, db = bdd_db ) c = cnx.cursor()
Comme expliqué précédemment, nous allons ici vérifier l’existence et la valeur du pointer pour ne pas reprendre le fichier complet puis ouvrir le fichier de log au niveau du pointer.
Nous verrons en fin d’article la possibilité de remettre ce pointer à 0 si vous avez un logrotate activé sur votre serveur.
# Vérification du pointer pour ne pas reprendre le fichier a 0 pointer = 0 verif = open(file_pointer,'r') for num in verif.xreadlines(): pointer = int(num) verif.close() # Ouverture du fichier de log et vérification du pointer logapache = open(log,'r') if pointer > 0: logapache.seek(pointer, 0)
Traitement du fichier ligne par ligne. A chaque ligne de log, le script va analyser l’url et déterminer, en fonction des regexp définies plus haut, si la requête est une tentative d’attaque ou non.
Dans cet exemple du script, les regexp de détection sont assez basiques, dans la partie 3 nous élaborerons des techniques plus poussées.
Si une attaque potentielle est détectée, alors elle sera enregistrée en BDD pour générer par la suite le flux RSS ou tout simplement consulter les différents enregistrements à partir d’une interface web comme nous le verrons dans la partie 2 de l’article.
# Traitement du fichier ligne par ligne k=1 for line in logapache.xreadlines(): # Initialisation des Variables data_ip = 'NULL' data_date = 'NULL' data_url = 'NULL' # IP field_ip = pattern_ip.search(line) if field_ip: data_ip = field_ip.group() # DATE field_date = pattern_date.search(line) if field_date: tmp_date = time.strptime(field_date.group(), "%d/%b/%Y:%H:%M:%S") data_date = time.strftime("%Y-%m-%d %H:%M:%S", tmp_date) # URL field_url = pattern_url.search(line) if field_url: data_method,data_url = (field_url.group().strip().split() + ['0','0'])[:2] # ATTAQUES POTENTIELLES field_faille_xss = p_faille_xss.search(data_url) field_faille_unicode = p_faille_unicode.search(data_url) field_faille_inc = p_faille_inc.search(data_url) field_faille_sql = p_faille_sql.search(data_url) field_faille_word = p_faille_word.search(data_url) if field_faille_xss or field_faille_unicode or field_faille_inc or field_faille_sql or field_faille_word: if field_faille_xss: faille_type = '1' elif field_faille_unicode: faille_type = '2' elif field_faille_inc: faille_type = '3' elif field_faille_sql: faille_type = '4' elif field_faille_word: faille_type = '5' c.execute("INSERT IGNORE veille_vuln (ip, url, log, temps, id_type) VALUES (%s, %s, %s, %s, %s)", (data_ip, data_url, line, data_date, faille_type)) k = k + 1
Une fois le fichier traité, nous allons successivement :
- Fermer la connexion MySQL
- Récupérer la position du pointer
- Fermer le fichier de log
# Fermeture de la connexion MySQL cnx.commit() c.close() # Recuperation position actuelle du pointer puis fermeture du fichier eof = logapache.tell() logapache.close() # Sauvegarde du pointer en cours new_verif = open(file_pointer,'w') new_verif.write(str(eof)) new_verif.close()
Si vous n’avez qu’un seul fichier de log que vous videz de temps en temps, il suffit de mettre en tâche cron le script veille-secu, mais si vous utilisez un logrotate, il faudra rajouter une étape.
Ci-dessous, on fait tourner le script toutes les 5min.
*/5 * * * * /usr/sbin/veille-secu -f /path/to/your/apache_log
Dans le cas d’une utilisation de logrotate, dans votre fichier de conf, on va ajouter au niveau de postrotate une commande permettant de remettre le pointer à 0 à chaque rotation de log.
postrotate if [ -f /var/run/apache2.pid ]; then /etc/init.d/apache2 restart > /dev/null echo 0 > /path/to/your/pointer fi endscript
Voilà, j’espère que ce premier article vous plaira sinon Tim risque de pas être content et il ne voudra plus venir me voir ^^
Tags: apache, PHP, python, rss, sécurité, veille
3 Réponses
Laisser un message

Sans quoi il y’a Snort qui est très complet : http://www.snort.org/
Il est vrai qu’il existe des IDS très complets et beaucoup plus performants, le but ici est surtout d’avoir un petit script rapide à mettre en place et qui marche sans avoir à configurer un IDS.
Les IDS sont très bien, snort notamment puisque c’est celui que tu cites, mais sa configuration, quand on ne connait pas, est beaucoup plus longue et prend beaucoup plus de temps si on veut éviter les faux-positifs, très nombreux sur snort.
De même snort permet de surveiller tout le traffic, ici on s’intéresse seulement aux requêtes web.
Cela dit, Snort est une très bonne solution ;)
Dites, d’ailleurs, c’est quoi un IDS?