2 points par GN⁺ 2025-04-30 | 1 commentaires | Partager sur WhatsApp
  • Un blogueur envoie une Zip Bomb basée sur gzip aux bots malveillants en réponse HTTP afin de réduire les scans de vulnérabilités, le spam et le scraping de contenu
  • En exploitant le flux Accept-Encoding: gzip, deflate utilisé par les navigateurs et crawlers légitimes, le serveur renvoie 200 OK et Content-Encoding: gzip aux requêtes suspectes
  • Un fichier gzip de 1 Mo grossit à environ 1 Go une fois décompressé, et un fichier de 10 Mo à environ 10 Go, ce qui épuise la mémoire de nombreux bots ou les pousse à abandonner la requête
  • Un middleware côté serveur vérifie les IP en liste noire et les motifs de spam ; si une condition correspond, il envoie un fichier Zip Bomb pré-généré puis met fin au traitement de la requête
  • Ce n’est pas une défense complète, car elle peut être facilement détectée ou contournée, mais c’est suffisant pour bloquer des bots simples qui perturbent le serveur par du crawling massif

Contexte : trafic de bots et usage des Zip Bomb

  • La majeure partie du trafic web provient de bots ; certains ont des usages légitimes, comme les lecteurs RSS, les crawlers de moteurs de recherche ou les bots d’IA à la recherche de nouveaux contenus
  • Le problème vient des bots malveillants opérés par des spammeurs, des scrapers de contenu et des hackers
    • Dans un ancien emploi, des bots ont trouvé une vulnérabilité WordPress et injecté un script malveillant sur le serveur
    • Ce serveur est ensuite devenu une partie d’un botnet utilisé pour des DDoS
    • L’un de ses premiers sites a été totalement exclu de Google Search à cause du spam généré par des bots
  • Après ces expériences, il a commencé à utiliser des Zip Bomb pour protéger ses serveurs des bots malveillants

Détourner la compression gzip comme moyen de défense

  • Une Zip Bomb est un petit fichier compressé qui se déploie en un fichier énorme lors de la décompression et met la machine sous pression
  • Aux débuts du web, la compression gzip a été introduite pour réduire les volumes de données transmis sur des connexions internet lentes
    • Réduire un fichier HTML de 50 Ko à 10 Ko permettait d’économiser 40 Ko de transfert
    • Sur une connexion téléphonique, cela pouvait faire passer le temps de téléchargement d’une page de 12 à 3 secondes
  • Lors d’une requête, le navigateur indique les méthodes de compression qu’il prend en charge via un en-tête
Accept-Encoding: gzip, deflate
  • Si le serveur prend aussi en charge la compression, il renvoie une version compressée des données attendues
  • Les bots de crawling web prennent eux aussi en charge la compression comme gzip pour collecter de gros volumes de données, et cette propriété est exploitée ici à des fins défensives

Réponse envoyée aux requêtes malveillantes

  • Le blog reçoit souvent des bots qui scannent des vulnérabilités de sécurité, et la plupart sont ignorés
  • Lorsqu’une requête semble chercher à injecter une entrée malveillante ou à explorer les réponses du serveur, une réponse gzip est renvoyée avec 200 OK
Content-Encoding: gzip
  • La taille du fichier envoyé varie selon la situation, entre 1 Mo et 10 Mo
    • Un fichier de 1 Mo passe à environ 1 Go lors de la décompression
    • Un fichier de 10 Mo passe à environ 10 Go lors de la décompression
  • En voyant l’en-tête, le bot suppose qu’il s’agit d’un fichier compressé et tente de le décompresser pour en lire le contenu
  • À mesure que le fichier continue à s’étendre, la mémoire peut être épuisée et le serveur ou le script peuvent planter
  • Pour les scripts particulièrement tenaces auxquels un fichier de 1 Mo ne suffit pas, il envoie un fichier de 10 Mo ; dans ce cas, le script s’arrête immédiatement

Exemple de création d’une Zip Bomb et de déploiement côté serveur

  • Créer une Zip Bomb implique d’assumer un risque, car cela peut faire planter ou endommager sa propre machine
  • Un fichier gzip qui se décompresse en 10 Go peut être généré avec la commande suivante
dd if=/dev/zero bs=1G count=10 | gzip -c > 10GB.gz
  • La commande se décompose ainsi
    • dd : commande de copie ou de transformation de données
    • if=/dev/zero : utilise comme entrée un fichier spécial produisant un flux infini d’octets nuls
    • bs=1G : définit la taille de bloc à 1 Go
    • count=10 : traite 10 blocs de 1 Go pour produire 10 Go de données nulles
    • gzip -c > 10GB.gz : compresse la sortie avec gzip et l’enregistre dans le fichier 10GB.gz
  • Dans ce cas, la taille du fichier résultant est d’environ 10 Mo
  • Côté serveur, un middleware a été ajouté pour vérifier si la requête en cours est malveillante
    • Une liste noire d’IP qui scannent de façon répétée l’ensemble du site est maintenue
    • Des motifs où un spam est posté puis suivi d’une nouvelle visite pour vérifier s’il a été publié servent aussi à la détection
if (ipIsBlackListed() || isMalicious()) {
    header("Content-Encoding: gzip");
    header("Content-Length: ". filesize(ZIP_BOMB_FILE_10G)); // 10 MB
    readfile(ZIP_BOMB_FILE_10G);
    exit;
}
  • Le coût est que, dans certaines situations, le serveur doit transmettre un fichier de 10 Mo
  • Quand un article devient viral, il abaisse à 1 Mo, et ce fichier reste selon lui efficace

Limites et périmètre d’usage

  • Une Zip Bomb n’est pas une défense complète
    • Elle peut être détectée facilement
    • Elle peut être contournée
    • Le client peut aussi ne lire qu’une partie du contenu
  • Malgré cela, c’est un outil suffisant pour bloquer des bots peu sophistiqués qui crawlent le web sans discernement et perturbent le serveur
  • Un exemple en fonctionnement peut être vu dans une relecture des logs serveur : this replay of my server logs

1 commentaires

 
GN⁺ 2025-04-30
Avis de Hacker News
  • Vers 2001, j’avais une ligne fixe à la maison et j’hébergeais diverses choses sur une machine Linux domestique.
    À cause d’une mise à jour de Windows NT, beaucoup de systèmes avaient activé une fonction de chiffrement opportuniste qui se connectait d’abord à un port précis pour négocier s/wan avant d’envoyer du trafic TCP ; je voyais souvent ce trafic dans le pare-feu, donc je n’y prêtais pas attention.
    Mais une machine en particulier essayait de se connecter toutes les quelques secondes, ce qui devenait vraiment pénible, et mes tentatives pour contacter l’administrateur ont échoué.
    Au final, je l’ai informé en mode « j’espère que ça ne posera pas de problème si je lance un nouveau service sur ce port », et comme je n’ai pas eu de réponse, j’ai lancé sur ce port un serveur qui lisait /dev/urandom, activait TCP_NODELAY et autres, puis poussait des données aléatoires aussi vite que possible.
    La machine NT mal configurée se connectait, avalait environ 5 secondes de données aléatoires, disparaissait, puis revenait 5 minutes plus tard prendre une nouvelle dose de dépassement de tampon, et ce schéma a duré plusieurs semaines avant qu’elle disparaisse complètement d’Internet.
    J’imagine parfois un administrateur se gratter la tête en se demandant pourquoi sa machine NT redémarrait sans cesse.

    • En tant que programmeur, il faut toujours fixer une limite supérieure à la quantité de données que l’on reçoit des autres.
      Chaque requête devrait avoir à la fois une limite de temps et une limite sur la quantité de données consommées.
    • À peu près à la même époque, une entreprise envoyait des fax de spam tous les vendredis, et ignorait les messages vocaux polis laissés à plusieurs reprises.
      J’ai donc créé un PDF de 100 pages où chaque page était un grand rectangle noir, puis je l’ai envoyé via une passerelle e-mail-vers-fax, nouvelle à l’époque ; moins d’une heure plus tard, j’ai reçu un appel furieux, et les fax se sont arrêtés.
    • Je me demande comment, à cette époque, on trouvait généralement les coordonnées de l’administrateur d’un client quelconque.
      Le passage « j’ai essayé de contacter l’administrateur de la machine et c’était courant » est particulièrement intéressant.
    • Une fois, j’ai bricolé à la va-vite la détection de panne de mon serveur RPi à la maison : il envoyait un ping à un domaine que je possédais, et en cas d’échec considérait que le réseau était coupé et redémarrait.
      Après avoir laissé expirer le domaine, ce RPi mourait toutes les 5 minutes ; j’ai cru à un problème d’alimentation, jusqu’à ce que je me souvienne plus tard de cette tâche CRON.
    • Vous seriez peut-être surpris d’apprendre que la plupart des installations NT qui fournissaient des services à cette époque n’avaient pratiquement aucun administrateur susceptible de remarquer ce qui se passait.
      Dans des milliers de cas, la raison même de faire tourner ce genre de service sur une machine NT était « pour ne pas avoir besoin d’administrateur » ; il ne faut pas sous-estimer ça.
      Dans les années 90 et au début des années 2000, j’ai mis en ligne beaucoup de serveurs, et la pratique standard dans l’industrie était globalement d’utiliser NT pour se passer d’administrateur.
  • Quand j’étais jeune, j’ai bêtement fait une blague sur ma page perso en mettant ln -s /dev/zero index.html.
    Les navigateurs de l’époque géraient très mal ça et se figeaient presque complètement ; parfois, cela plantait même le système client avec.
    Plus tard, il me semble que les navigateurs ont commencé à vérifier le contenu réel et à interrompre ce genre de requêtes.

    • Une fois, j’ai alimenté un encodeur avec la même ligne de macroblocs en boucle pour créer un JPEG 64k×64k.
      Ce n’est que plusieurs années plus tard que j’ai enfin pu l’ouvrir.
    • Je me demande s’il serait possible de créer, sur squashfs, un fichier HTML de 500 To avec les bons en-têtes, d’y mettre un contenu infini sans balise de fermeture, puis de faire en sorte que le serveur n’annonce pas la taille du fichier avant le téléchargement.
      Des idées ?
    • Ça me rappelle le favicon.ico qui faisait planter les navigateurs.
      C’était sans doute celui-ci : https://freedomhacker.net/annoying-favicon-crash-bug-firefox...
    • J’espère que ce n’était pas dans un contexte où la bande passante était facturée au KiB.
    • Il est peut-être temps de créer un périphérique /dev/zipbomb.
  • Aujourd’hui, presque tous les navigateurs acceptent zstd et brotli, donc ce type de bombe pourrait être encore plus efficace maintenant.
    This ancien commentaire montrait un impressionnant taux de compression de 1,2M:1, et zstd seems to be doing even better.
    Cela dit, les bots ne prennent pas forcément en charge les standards de compression modernes.
    À l’inverse, ça peut être une bonne méthode de blocage des bots : comme tous les navigateurs modernes prennent en charge zstd, l’imposer aux user-agents de navigateurs qui ne sont pas sur liste blanche pourrait automatiquement perturber les scrapers.

    • En fait, j’utilise la compression pour filtrer les bots dans ma démo Datastar one million checkboxes[1].
      Elle repose fortement sur le streaming de la vue utilisateur complète à chaque interaction, et avec brotli par-dessus SSE, on obtient facilement des taux de compression de 200:1[2].
      Le problème, c’est qu’un acteur malveillant peut demander un flux non compressé.
      Comme brotli est pris en charge par 98 % des navigateurs, je n’envoie pas de données aux clients qui ne prennent pas en charge la compression brotli, et comme beaucoup de scrapers et de bots ne la prennent pas non plus en charge, cela fonctionne plutôt bien.
      [1] démo checkboxes
      https://checkboxes.andersmurphy.com
      [2] article sur brotli SSE
      https://andersmurphy.com/2025/04/15/why-you-should-use-brotl...
    • En imbriquant gzip dans gzip, on obtient quelque chose d’encore plus petit.
      C’est parce que, dans la première génération gzip, le bloc de données 0 compressé est lui-même à faible entropie, et du zst imbriqué réduit un fichier de 10 Go à 99 octets.
    • Je me demande comment mon navigateur réagirait s’il recevait une telle bombe.
      Je n’ai pas envie de tester moi-même.
    • gzip est partout, et peut embêter tous les crawlers.
  • L’anecdote de l’ancien boulot, où un bot avait trouvé une vulnérabilité WordPress et placé un script malveillant sur le serveur, s’écarte un peu du sujet, mais elle est amusante.
    Ça m’a plutôt rassuré de découvrir que je n’étais pas le seul à voir un shell PHP se déployer comme par magie sur un serveur une heure à peine après l’installation de WordPress.

    • Quand on reprend le site WordPress d’un client, ça devient vite : « oh, il y a 3 shells PHP aux noms en chaînes aléatoires ».
      Jamais moins de 3, c’est toujours garanti.
    • Si vous voulez garder votre santé mentale, n’hébergez pas WordPress vous-même.
      Même si ce n’est pas dans la première heure, le jour où vous oublierez un patch, ça finira par exploser.
    • Je n’ai jamais hébergé WordPress, mais dès qu’on expose un serveur HTTP sur Internet, des requêtes du genre /wp-login arrivent.
      C’est aussi un bon moyen de repérer les bots : si je vois une IP demander des chemins liés à des CMS connus, je l’envoie directement dans un trou iptables.
    • WordPress est une excellente porte dérobée, avec en plus des fonctions de CMS intégrées.
    • Je m’en suis déjà servi pour enseigner le DevOps.
      Quand on dit « déployez votre premier serveur nginx hello world », on commence aussitôt à voir des requêtes bizarres dans les logs.
  • J’ai fait quelque chose de similaire avec ssh : j’avais trouvé un moyen de tuer les clients ssh qui tentaient de deviner le mot de passe root.
    En contrepartie, plusieurs script kiddies ont lancé des DDoS contre mon petit serveur, et j’ai fini par passer à une approche consistant à identifier les acteurs manifestement malveillants et à bloquer leurs IP avec des règles de pare-feu.
    Cela dit, avec IPv6, ça devient plus difficile.
    Si vous créez vos propres pages web, vous pouvez aussi y mettre une bombe zip via un lien invisible à l’œil humain.
    Par exemple une ancre en texte blanc sur fond blanc, sans mise en évidence au survol ni au clic : les bots la téléchargeront et l’inspecteront, tout comme les crawlers et les scrapers d’IA.

    • J’ai utilisé une variante de cette méthode sur le formulaire de demande de compte de mon serveur fediverse.
      Le problème venait de bots très simples qui parcouraient le web et soumettaient du spam grossier à tous les formulaires qui semblaient pouvoir être publiés.
      Au début, j’ai ajouté un CAPTCHA simple avec des caractères déformés, ce qui a bloqué beaucoup de bots, mais pas tous.
      En regardant les logs du serveur, j’ai vu que ces bots n’envoyaient rapidement que trois requêtes : la page contenant le formulaire, l’image du CAPTCHA, puis la requête POST des données du formulaire, sans jamais charger le CSS ni le JS.
      J’ai donc ajouté quelques champs supplémentaires au formulaire, masqués via CSS, puis fait échouer la requête et bloqué la session si quoi que ce soit était envoyé dans ces champs.
      J’ai aussi transformé l’image du CAPTCHA en arrière-plan CSS et remplacé src par une image transparente : le spam s’est complètement arrêté, et les vrais utilisateurs n’ont rien remarqué.
    • Si vous voulez empêcher ce genre de comportement, ça vaut le coup de regarder ceci :
      https://github.com/skeeto/endlessh
    • Si le lien est invisible à l’œil humain, je me demande ce qu’il en est des utilisateurs de lecteurs d’écran.
    • Je ne vois pas pourquoi le blocage par pare-feu serait plus difficile en IPv6.
      Entre les deux, ça me paraît plutôt plus simple.
    • Ce genre de lien peut être visible pour les personnes qui utilisent un navigateur texte, un lecteur d’écran, un bookmarklet listant les liens de la page, etc.
  • Les bombes zip sont amusantes.
    J’ai déjà trouvé, dans un produit de sécurité, une vulnérabilité qui faisait qu’il n’effectuait pas correctement l’analyse antimalware sur les archives zip dépassant une certaine taille, ou sur les fichiers qui en contenaient.
    En pratique, en mettant une bombe zip dans un document Office XML, le produit pouvait laisser passer le fichier OOXML même s’il contenait un malware facilement identifiable.

    • Beaucoup d’EDR connus ont encore des problèmes liés à la taille des fichiers.
  • J’ai essayé de déployer ça à la place de mon script de honeypot habituel, mais ça ne semble pas très bien fonctionner.
    Dans les logs du serveur web, je vois que les bots ne téléchargent pas tout le poison de 10 Mo : ils coupent à différentes longueurs.
    Jusqu’ici, je n’en ai vu aucun récupérer plus d’environ 1,5 Mo.
    Ou alors est-ce que ça fonctionne quand même ? Ils sont peut-être en train de décoder le flux à la volée et de mourir.
    Par exemple, si le log indique qu’ils ont lu 1,5 Mo, ils ont peut-être planté en le décodant à la volée en 1,5 Go en mémoire.
    Impossible de le vérifier.

    • Ça peut valoir le coup d’essayer un labyrinthe de contenu.
      Un contenu généré à l’infini, avec plein de références vers d’autres pages générées, par exemple.
      Ça peut aider contre un simple wget et contre les bots, jusqu’à ce qu’ils s’adaptent.
      À noter que je suis plutôt du côté des bots, mais ça ne me dérange pas d’aider.
    • Il faudrait peut-être rendre la taille du fichier semi-aléatoire.
      Certains bots ont probablement une limite stricte sur la taille des ressources qu’ils téléchargent.
      Comme beaucoup sont des bots pénibles d’entraînement/scraping pour LLM, même une bombe zip de 800 Ko ne les tuera peut-être pas, mais elle peut quand même leur faire gaspiller des ressources de calcul.
    • S’ils reviennent, c’est qu’ils l’ont détectée et l’évitent ; s’ils ne reviennent pas, c’est qu’ils ont planté, donc mission accomplie.
  • Il vaut la peine de préciser qu’il ne s’agit pas d’un fichier zip classique, mais d’une bombe gzip.
    Ce n’est pas une méthode qui fait tomber un antivirus avec des zips imbriqués : ça se comporte comme une page web compressée ordinaire.

  • Il y a quelque temps, une partie de l’infrastructure de contournement de la censure du Tor Project était hébergée sur le même site que l’article de blog sur les bombes zip[0].
    L’un de ces fichiers zip a été crawlé par Google et s’est retrouvé dans une liste de domaines malveillants, ce qui a cassé une partie assez importante de l’outil Snowflake de Tor.
    Il a fallu plusieurs semaines pour corriger le problème[1].
    [0] https://www.bamsoftware.com/hacks/zipbomb/
    [1] https://www.bamsoftware.com/hacks/zipbomb/#safebrowsing

  • Pour protéger les uploads de mon application, je créais des partitions temporaires de disque à taille fixe d’environ 10 Mo chacune, puis j’y décompressais les fichiers.
    Si quelqu’un envoyait un fichier trop gros, les dégâts restaient confinés à cet endroit.

    • unzip -p | head -c 10MB
    • Donc, au lieu de simplement éviter de décompresser des fichiers absurdement gros, tu as vraiment partitionné le disque ?