WordPress DoS attack threat

One third of all websites may be under the DoS attack at any time

Houston, we have a problem! A serious problem that theoretically can affect one-third of all websites on the Internet. Recently Israeli security researcher Barak Tawily found a WordPress vulnerability that can lead to a massive DoS attack. DoS attack is a type of cyber-attack when an attacker drains network or server resources by flooding it with an enormous amount of requests. Every request needs some resources, but if you’re capable of making a lot of these requests or you find a way to drain more resources with fewer requests you’ll finally make the network or server inaccessible for the time of the attack.

Do not confuse DoS attacks with DDoS attacks, DoS (denial-of-service attack) attacks run from a single source of requests and DDoS (distributed denial-of-service attack) need more than one request sources. In this case, we are talking about attacks that are possible to execute from a single request source (for example one computer). The success of a DoS attack is directly dependent on how many requests a hacker can generate and how much it consumes server or network resources. Usually, DDoS attacks are more efficient than DoS attacks. But in this case, a single attacker could make a significant load on the server and create the real denial-of-service situation.

WordPress vulnerability to DoS attacks at the application level

Let’s look closer why WordPress is vulnerable to DoS attacks. Barak Tawily in his research analyzed how WordPress loads JavaScripts and CSS files. WordPress has a mechanism to lower the amount of request with a simple trick, load several files with only one request. For example load-scripts.php loads necessary JavaScript files and load-styles.php loads necessary CSS files. In this case, the browser makes only two requests and gets way more files back.

The file responsible for loading all the JavaScript files load-scripts.php uses file script-loader.php which has hardcoded list of available JavaScripts. You can see the part of script-loader.php file below.

	// full jQuery UI
	$scripts->add( 'jquery-ui-core', "/wp-includes/js/jquery/ui/core$dev_suffix.js", array('jquery'), '1.11.4', 1 );
	$scripts->add( 'jquery-effects-core', "/wp-includes/js/jquery/ui/effect$dev_suffix.js", array('jquery'), '1.11.4', 1 );

	$scripts->add( 'jquery-effects-blind', "/wp-includes/js/jquery/ui/effect-blind$dev_suffix.js", array('jquery-effects-core'), '1.11.4', 1 );
	$scripts->add( 'jquery-effects-bounce', "/wp-includes/js/jquery/ui/effect-bounce$dev_suffix.js", array('jquery-effects-core'), '1.11.4', 1 );
	$scripts->add( 'jquery-effects-clip', "/wp-includes/js/jquery/ui/effect-clip$dev_suffix.js", array('jquery-effects-core'), '1.11.4', 1 );
	$scripts->add( 'jquery-effects-drop', "/wp-includes/js/jquery/ui/effect-drop$dev_suffix.js", array('jquery-effects-core'), '1.11.4', 1 );
	$scripts->add( 'jquery-effects-explode', "/wp-includes/js/jquery/ui/effect-explode$dev_suffix.js", array('jquery-effects-core'), '1.11.4', 1 );
	$scripts->add( 'jquery-effects-fade', "/wp-includes/js/jquery/ui/effect-fade$dev_suffix.js", array('jquery-effects-core'), '1.11.4', 1 );
	$scripts->add( 'jquery-effects-fold', "/wp-includes/js/jquery/ui/effect-fold$dev_suffix.js", array('jquery-effects-core'), '1.11.4', 1 );
	$scripts->add( 'jquery-effects-highlight', "/wp-includes/js/jquery/ui/effect-highlight$dev_suffix.js", array('jquery-effects-core'), '1.11.4', 1 );
	$scripts->add( 'jquery-effects-puff', "/wp-includes/js/jquery/ui/effect-puff$dev_suffix.js", array('jquery-effects-core', 'jquery-effects-scale'), '1.11.4', 1 );
	$scripts->add( 'jquery-effects-pulsate', "/wp-includes/js/jquery/ui/effect-pulsate$dev_suffix.js", array('jquery-effects-core'), '1.11.4', 1 );
	$scripts->add( 'jquery-effects-scale', "/wp-includes/js/jquery/ui/effect-scale$dev_suffix.js", array('jquery-effects-core', 'jquery-effects-size'), '1.11.4', 1 );
	$scripts->add( 'jquery-effects-shake', "/wp-includes/js/jquery/ui/effect-shake$dev_suffix.js", array('jquery-effects-core'), '1.11.4', 1 );
	$scripts->add( 'jquery-effects-size', "/wp-includes/js/jquery/ui/effect-size$dev_suffix.js", array('jquery-effects-core'), '1.11.4', 1 );
	$scripts->add( 'jquery-effects-slide', "/wp-includes/js/jquery/ui/effect-slide$dev_suffix.js", array('jquery-effects-core'), '1.11.4', 1 );
	$scripts->add( 'jquery-effects-transfer', "/wp-includes/js/jquery/ui/effect-transfer$dev_suffix.js", array('jquery-effects-core'), '1.11.4', 1 );

	$scripts->add( 'jquery-ui-accordion', "/wp-includes/js/jquery/ui/accordion$dev_suffix.js", array('jquery-ui-core', 'jquery-ui-widget'), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-autocomplete', "/wp-includes/js/jquery/ui/autocomplete$dev_suffix.js", array( 'jquery-ui-menu', 'wp-a11y' ), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-button', "/wp-includes/js/jquery/ui/button$dev_suffix.js", array('jquery-ui-core', 'jquery-ui-widget'), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-datepicker', "/wp-includes/js/jquery/ui/datepicker$dev_suffix.js", array('jquery-ui-core'), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-dialog', "/wp-includes/js/jquery/ui/dialog$dev_suffix.js", array('jquery-ui-resizable', 'jquery-ui-draggable', 'jquery-ui-button', 'jquery-ui-position'), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-draggable', "/wp-includes/js/jquery/ui/draggable$dev_suffix.js", array('jquery-ui-mouse'), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-droppable', "/wp-includes/js/jquery/ui/droppable$dev_suffix.js", array('jquery-ui-draggable'), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-menu', "/wp-includes/js/jquery/ui/menu$dev_suffix.js", array( 'jquery-ui-core', 'jquery-ui-widget', 'jquery-ui-position' ), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-mouse', "/wp-includes/js/jquery/ui/mouse$dev_suffix.js", array( 'jquery-ui-core', 'jquery-ui-widget' ), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-position', "/wp-includes/js/jquery/ui/position$dev_suffix.js", array('jquery'), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-progressbar', "/wp-includes/js/jquery/ui/progressbar$dev_suffix.js", array('jquery-ui-core', 'jquery-ui-widget'), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-resizable', "/wp-includes/js/jquery/ui/resizable$dev_suffix.js", array('jquery-ui-mouse'), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-selectable', "/wp-includes/js/jquery/ui/selectable$dev_suffix.js", array('jquery-ui-mouse'), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-selectmenu', "/wp-includes/js/jquery/ui/selectmenu$dev_suffix.js", array('jquery-ui-menu'), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-slider', "/wp-includes/js/jquery/ui/slider$dev_suffix.js", array('jquery-ui-mouse'), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-sortable', "/wp-includes/js/jquery/ui/sortable$dev_suffix.js", array('jquery-ui-mouse'), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-spinner', "/wp-includes/js/jquery/ui/spinner$dev_suffix.js", array( 'jquery-ui-button' ), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-tabs', "/wp-includes/js/jquery/ui/tabs$dev_suffix.js", array('jquery-ui-core', 'jquery-ui-widget'), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-tooltip', "/wp-includes/js/jquery/ui/tooltip$dev_suffix.js", array( 'jquery-ui-core', 'jquery-ui-widget', 'jquery-ui-position' ), '1.11.4', 1 );
	$scripts->add( 'jquery-ui-widget', "/wp-includes/js/jquery/ui/widget$dev_suffix.js", array('jquery'), '1.11.4', 1 );

The load-scripts.php file receives a parameter called load[]. For example, if you send a request with the parameter value jquery-ui-core, you’ll get the response with the JavaSCript module ‘jQuery UI Core’ that was requested. That load[] parameter is an array, which means that it is possible to provide multiple values with your request and you’ll get multiple JavaScript modules as a response.

Now the funny part, there are 181 values it means you can request 181 JavaScript module with a single request, and that is the problem. What makes this issue even worse? This feature was designed only for the admin pages, but it’s also available in the wp-login.php page, and that means that unauthenticated user can send the requests. Now we can see the whole problem. Any visitor that can access the WP login page can send crafted requests with all 181 parameters included to get all those 181 JavaScript modules as a response.

To make a proof of concept Barak Tawily used a Python script that generated same crafted requests as quickly as possible only by using one computer and he managed to reach the denial-of-service condition, he made a testing site inaccessible.

WordPress team response and conclusion

Barak Tawily notified the WordPress development team about this issue on the HackerOne website, and he received such answer: “This kind of thing should be mitigated at the server or network level rather than the application level, which is outside of WordPress’s control.”

I don’t want to judge this response, but on the other hand, this issue could cause some trouble to owners of the WordPress sites. Well, to be more precise to one-third websites on the Internet since WordPress has such capitalization. Another question that does not give me peace. Is it necessary to allow such request for non-authenticated users on the WP login page?

We have added this issue to the database of WordPress vulnerabilities, but I’m not sure this problem will be solved by WordPress, at least their response on HackerOne gives a hint. Let the situation settle down, and we will see what happen next.

Leave a Reply

Your email address will not be published. Required fields are marked *