Script de mitigation d’attaque DDOS

Si vous suivez Hackademics sur Twitter, vous savez probablement que nous avons subit une attaque DDOS hier soir.

Les admins et moi même avons rapidement réglé le soucis et la situation est bien vite rentrée dans l’ordre malgré un filtrage d’adresse IP un tout petit peu trop restrictif entre hier 23h et ce matin 6h.

Je ferai dans les quelques jours à venir un article plus détaillé sur le déroulement de l’attaque, sa découverte et sa mitigation mais aujourd’hui, j’aimerais vous présenter le programme que j’ai écrit pour éviter de re-mettre en place des blocages trop restrictifs dans le cas d’une future occurence.

Je définirais ce code comme un outils de mitigation d’attaque DDOS dans la mesure où il est capable de :

  1. Grouper les logs de connexion sur un serveur NGINX en série temporelle pour définir quand l’attaque à commencé (à partir de cette tranche temporelle, le nombre de connexion devrait augmenter très significativement)
  2. Retrouver les adresses IP s’étant connecté à votre serveur dans une tranche temporelle que vous définissez

Un exemple d’utilisation lors d’une attaque serait alors de :

  1. Découvrir le début de l’attaque via le premier point de la liste précédente
  2. Relever les IPs attaquantes via le second point de la liste précédente tout en fixant le début au début de l’attaque et la fin à un moment que vous choisissez. Remarquez quand même que mettre un point de fin éloigné du point de début augmente le nombre d’IPs attaquantes que vous allez sélectionner mais augmente également le nombre d’IPs légitimes que vous prendrez avec
  3. Créer des règles firewall de banissement sur les IPs découvertes

Le code en lui même est disponible en fin d’article et sur GitHub

Voici de plus le manuel du programme (qui est écrit en Python 3 et sans dépendances) :

usage: mitigate_DDOS.py [-h] -f NGINX_LOG_FILE [-a] [-t TIME] [-i]
                        [-b BEGIN_TIMESTAMP] [-e END_TIMESTAMP]

Analyzes web server logs to find begin and mitigate DDOS attack

optional arguments:
  -h, --help          show this help message and exit
  -f NGINX_LOG_FILE   The nginx log file
  -a                  Analysis task
  -t TIME             Time split for log blocks (default 10)
  -i                  Get IPs between two timestamps
  -b BEGIN_TIMESTAMP  Begin timestamp (in seconds) for IP gathering
  -e END_TIMESTAMP    Begin timestamp (in seconds) for IP gathering
Le code du programme

Notez que le code pourrait être modifié sur GitHub mais ne le sera dans ce cas pas sur cette page

"""Script to mitigate a ddos attack by analyzing web log log file"""


import os
import sys
from collections import namedtuple
import datetime
import argparse


LogLine = namedtuple("LogLine", "ip time")


class NginxLogDao():
    """Data access object for NginxLog"""

    def __init__(self, filename):
        """Initialises this with given filename"""

        assert filename is not None
        assert os.path.exists(filename)

        self.filename = filename

    def log_lines(self):
        """Reads logs lines and returns them as named tuples"""

        with open(self.filename) as f:
            for line in f:
                ip = line.split("-")[0].strip()
                date_str = line.split("[")[1].split("]")[0].split(" ")[0]
                date = datetime.datetime.strptime(
                    date_str, '%d/%b/%Y:%H:%M:%S')
                yield LogLine(ip=ip, time=date)

    def time_grouped(self, time_size=10):
        """Returns log lines grouped in time groups with blocks 
           of time_size seconds"""

        block = None
        block_begin = None
        for log_line in self.log_lines():
            if block_begin is None or log_line.time > block_begin \
                    + datetime.timedelta(seconds=time_size):
                if block_begin is not None:
                    yield block

                block_begin = log_line.time
                block = list()

            block.append(log_line)

    def ips_between(self, dt1, dt2):
        """Returns set of ip addresses between two timestamps"""

        ips = set()
        for log_line in self.log_lines():
            if dt1 <= log_line.time <= dt2: 
                ips.add(log_line.ip) 
            elif log_line.time > dt2:
                break

        return list(ips)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description='Analyzes web server logs to find begin and mitigate DDOS attack')
    parser.add_argument('-f', action="store", dest="nginx_log_file",
                        help="The nginx log file", required=True)
    parser.add_argument('-a', action="store_true", dest="analyze",
                        help="Analysis task")
    parser.add_argument('-t', action="store", dest="time",
                        help="Time split for log blocks (default 10)",
                        default=10)
    parser.add_argument('-i', action="store_true", dest="get_ip",
                        help="Get IPs between two timestamps")
    parser.add_argument('-b', action="store", dest="begin_timestamp",
                        help="Begin timestamp (in seconds) for IP gathering")
    parser.add_argument('-e', action="store", dest="end_timestamp",
                        help="Begin timestamp (in seconds) for IP gathering")
    params = parser.parse_args()

    if params.analyze:
        time = int(params.time)
        blocks = NginxLogDao(
            params.nginx_log_file).time_grouped(time_size=time)
        for block in blocks:
            block_start = block[0].time
            timestamp = int(block_start.timestamp())
            print("{} - {} - {}".format(block_start, timestamp, len(block)))
    elif params.get_ip:
        if params.begin_timestamp is None or params.end_timestamp is None:
            print("With option -i you must provide option -b and -e")
            sys.exit(1)

        begin_timestamp = datetime.datetime.fromtimestamp(int(params.begin_timestamp))
        end_timestamp = datetime.datetime.fromtimestamp(int(params.end_timestamp))

        ips = NginxLogDao(params.nginx_log_file).ips_between(begin_timestamp,
                                                             end_timestamp)
        for ip in ips:
            print(ip)

Cet article est également apparu sur hackademics.fr

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *