SQL injections on WordPress websites

A little while back, an attacker from Eastern Europe attempted to break into our main website. The attempt was ultimately unsuccessful due to our defenses, and no damage of any kind was caused.

This writeup will hopefully help others in case they face their own attacks. It might also be of interest to those who want to know how our IT infrastructure is set up.


In the evening a short while ago, my email started going off like crazy, letting me know that comments were being made on our main website. The comments looked odd. They were being made every fifteen seconds, by an anonymous user, all from the same IP address. They contained strings like this:

DBMS_PIPE.RECEIVE_MESSAGE(CHR(99)||CHR(99)||CHR(99),15)

There were a few things about this that were alarming.

First: our main website doesn’t require commenting functionality. It has it, but I thought it was disabled. I was wrong.

Second: the frequency of the comments was extremely high. That is suspicious all on its own.

Third: most people with experience in the software world will instantly recognise that string for what it is: a SQL injection attack.

We’ll get into what a SQL injection attack is. But I knew from my experience that a successful SQL injection attack could lead to data breaches, site defacement, and perhaps even direct monetary damage. Any one of these comments represented quite a large threat.

And they were being attempted, right in front of my eyes, every fifteen seconds.


Many websites have a writable database of information. These databases contain all sorts of information. In the case of our website, this database includes things like photos, the actual words on our website, and email addresses of our supporters.

This information has to be sorted and stored in such a way that it can be retrieved quickly and reliably. Many websites using something called a “SQL database” to do this.

It’s not particularly relevant what SQL is, or what a SQL database is, in order to understand a SQL injection attack. So, we’re going to skip over too many technical details. What matters is how websites interact with SQL databases.

To retrieve information from a SQL database, we use something called a “query”. A query is a short line of code that the SQL database executes. Queries can do all sorts of things:

  • Retrieve information (like photos, words, email addresses, or anything in it)
  • Add new information
  • Delete certain pieces of information
  • Delete everything, permanently
  • And plenty more

So, let’s say that our website needs to get the words that we’ve typed up for the Adept page. The website would do a SQL query looking up those words; the SQL database would find them and hand them back to the website, which then presents them. Pretty straightforward.


Here’s the key problem: there are quite a few websites that will ask their SQL databases to execute any SQL queries that they find.

If, for instance, our website were to execute a SQL query that it happens to find in a comment left by a stranger on the Internet, then that would mean that anybody who comments on our website could potentially execute any SQL query that they wanted to. That would be very bad.

And that’s what our attacker was attempting to do.


Every fifteen seconds, the attacker posted a new comment on our website. It contained one of the following strings:

1-1 OR 668=(SELECT 668 FROM PG_SLEEP(15))–
1-1)) OR 128=(SELECT 128 FROM PG_SLEEP(15))–
1*DBMS_PIPE.RECEIVE_MESSAGE(CHR(99)||CHR(99)||CHR(99),15)
5550’XOR(555*if(now()=sysdate(),sleep(15),0))XOR’Z

There were quite a few others. This is just a small sample size.

If our website interpreted any of these as a valid SQL query and ran it on our database, it would let our attacker know that our website was vulnerable to a SQL injection, and thus our website would be compromised.


We had a few defenses up and running to mitigate this. We also put up a few new defenses to stop these kinds of attacks from even starting.

Probably the most important thing is: all of our software services are up-to-date. We never, ever run services that are old, because new software vulnerabilities are being discovered all the time, and patches are released to mitigate them.

In all likelihood, a SQL injection attack was never going to succeed on our website because any such vulnerabilities were patched long ago.


I also added the following PHP code to our website:

add_action('admin_init', function () {
    foreach (get_post_types() as $post_type) {
        if (post_type_supports($post_type, 'comments')) {
            remove_post_type_support($post_type, 'comments');
            remove_post_type_support($post_type, 'trackbacks');
        }
    }
});

add_filter('comments_open', '__return_false', 20, 2);
add_filter('pings_open', '__return_false', 20, 2);

add_filter('comments_array', '__return_empty_array', 10, 2);

This code disables comments on WordPress websites. It’s not an effective solution for WordPress installations that rely on comments to operate, but ours doesn’t, so it works perfectly.


Altogether, this attack lasted eleven minutes. It amounted to little more than a nuisance to clean everything up, and nothing was ever really in danger.

Still, it served as a stark reminder to me – and hopefully, to other sysadmins who read this – that keeping things up-to-date is important, and efforts to set up monitoring systems are worth it when things start getting out of control.