Mitigating CVE-2018-6389 WordPress DoS attack with lighttpd

Early in 2018, Barak Tawily published a possible DoS attack for WordPress, that basically works by requesting all possible scripts on the /wp-admin/load-scripts.php, a script that fetches and concatenates javascript files — there’s also a load-styles.php file that does the same for styles.

His vulnerability report was rejected by the WordPress team, on the account that this type of attack should be mitigated at the server or network level… so how do you do that using lighttpd?

Actually it’s pretty easy using mod_evasive, a “very simplistic module to limit connections per IP”, as advertised on the lighttpd docs.

First, you must make sure that mod_evasive it’s enabled on the server.modules block:

server.modules = (
  "mod_access",
  "mod_alias",
  "mod_compress",
  "mod_redirect",
  "mod_rewrite",
  "mod_accesslog",
  "mod_evasive"
)

Then, on the main lighttpd config file you can add the following:

$HTTP["url"] =~ "/wp-admin/load-(styles|scripts).php(.*)" {
  evasive.max-conns-per-ip = 8
}

This will effectively limit the amount of allowed connections to 8 by IP. Of course, you can adjust that value to whatever you need; 8 connections by IP it’s plenty enough for a “normal” editor use.

You can test if it’s working by “attacking” your server with siege or ab or your favourite benchmarking/load testing tool and checking your lighttpd error log, where this should appear:

2018-03-22 21:05:53: (mod_evasive.c.183) 192.168.33.1 turned away. Too many connections.

After testing, you might also want to add this to the lighttpd config:

evasive.silent = "enabled"

… that way, blocked IPs won’t be logged on the errors.log (which, on its own could trigger a DoS by repeatedly writing to the log file)

If you’re using nginx with HTTP/2, there’s an even better way.

Un-breaking lighttpd’s broken mod_access

A client let us know that the server where her company’s site was hosted had an unusually high load.

After checking the access log for the web server, it was clear that the cause was repeated access attempts at a single URL, which was not essential to the site. So I though this should be easy, I’ll just block the request in the web server config. Unfortunately, they were using a very outdated version of lighttpd, so it wasn’t that easy.

It seems that older lighttpd builds had several bugs with mod_access, but the worst in our case was that instead of blocking the request and send a 403 Forbidden, it passed the request on to the 404 error handler, and this loaded the entire app enviroment.

So here’s what I did. The lighttpd config looked like this:

$HTTP["url"] =~ "^/foobar.php" {
    url.access-deny = ("")
    server.error-handler-404 = "/403.php"
}

… so request to foobar.php would be handled by 403.php. And then, 403.php:

<?php header('HTTP/1.0 403 Forbidden'); ?>
<h1>Forbidden</h1>

Very silly, but effective. Just because status codes matter.

Sharing a single WordPress codebase for multiple multisite installations

Ok; the title seems a tongue-twister, but the situation it’s:

  • One of our clients, a rather large university, needed to create independent sites for each of its faculties aaaand we love WordPress, so it was clear that we were going to use it as their CMS
  • Each of these faculties had to be able to create and manage sites for their pregrad careers, postgrads and research centers, so instead of  separate installations for every site or just one huge multisite setup, we chose to set a multisite network for each of the faculties, where they could add and manage their sub-sites as needed
  • The codebase for all the sites it’s the same; they’re all part of the same project and all features are shared. Each faculty has a default theme (which it’s a child theme from a shared parent) and they all use the same plugins
  • Every faculty lives on a different subdomain, which we could use as a unique identifier for every site, so all sites had URLs like:
    • http://subdomain.somesite.com
    • http://othersubdomain.somesite.com
    • http://anothersubdomain.somesite.com

At one moment we had separate WordPress installs for each faculty, but sharing a single installation (multi tenancy) has some benefits, such as:

  • On the previous setup, we needed to update various directories from an SVN repo; if something went wrong updating one of these (because someone f*cked up the permissions or something like that) we would end up with different versions on the different sites
  • Keeping a single set of WordPress files should improve performance for an opcode cache such as APC or the built-in Zend Optimizer in PHP 5.5
  • Well… it just makes sense, doesn’t it?

So, how to do it?

First, you’ll need to tweak your wp-config.php file just a little bit. What’s important here it’s:

  • Getting the subdomain for the site that the user it’s viewing, which we’ll use as a unique identifier for connecting to the database and configuring the uploads directory
  • Set wp-config to use wp-content/sunrise.php, where we can identify the blog (sub-site) that the visitor requested, and map that to the correct uploads directory
  • Define the database connection constants used by WordPress for the requested site. Since all of us in the development team had local copies and slightly different setups, we did this on a local config called local-config.php

Of course, this assumes that you need to follow some sort of convention for the names of the databases and the uploads directories, and all canonical URLs for the sites are set to the subdomain that you’ll use as your id.

Both the wp-config.php and wp-content/sunrise.php were included on the project repository, along with a local-config-sample.php that should be used to define everything that was specific to the local enviroment.

The local-config.php that’s included defines:

  • DB_NAME, DB_USER, DB_PASSWORD, DB_HOST
  • DOMAIN_CURRENT_SITE which it’s needed by WordPress on Multisite setup
  • Also, it could be used to define other constants such as WP_DEBUG and related

The wp-content/sunrise.php we used was very similar to the one used by the WordPress MU Domain Mapping plugin, but with one important difference: we needed to define BLOGUPLOADDIR for getting the uploads from the correct site and subsite.

You can check these snippets on GitHub for a working example:

Continue reading “Sharing a single WordPress codebase for multiple multisite installations”

Install Dropbox On Ubuntu Server (10 & 11)

This post will help you install the Linux Dropbox client on your headless Ubuntu Server and link it up to your Dropbox account. Unlike the process of mounting an S3 bucket we looked at before the Dropbox approach is a much better solution for sharing files. If you’re a daily Dropbox user you’ll quickly get hooked on the convenience of having your servers in the same file sharing loop as all your other Dropbox connected devices!

Originally posted on March 21, 2012 at 11:50AM at Install Dropbox On Ubuntu Server (10 & 11)