Just a quick one…. I noticed in my webserver logs, a whole bunch of directory walk “script kiddie” exploit attempts to various wordpress sites on my server, attempting to retrieve my wordpress configuration file: wp-config.php.

A directory walk attack is where someone will attempt to use a download feature of some plugin or other in attempt to trick it to retrieve a different file, by passing ../ before the file name. E.g.

GET /wp-admin/admin-ajax.php?action=revslider_show_image&img=../wp-config.php

None of these exploits was successful, since this is an obvious approach which should be sanitised out of inputs, but part of having a secure system is the concept of strength in depth and every programmer makes mistakes.

So, I knocked together a quick modsecurity rule:

SecRule ARGS "(\.\.\/)+wp-config.php"\
  "phase:1,log,deny,status:503,msg:'Attempt to download wp-config.php via the GET line'"

Which seems to shut this one exploit down. HTH 🙂

Spam comes in may forms.

I had been noticing some odd traffic appearing in my referrer logs from “buttons-for-website.com”, and a few other places. Odd, I thought, but I wasn’t too concerned.

A client recently asked me about it, since similar traffic was starting to appear in their analytics for a brand new site. I did a little bit of research, and it turns out that this is actually a spam attack.

Basically, the spammer hits your site and sets a referrer header containing a url and their spam message (keywords + another url, usually). Since a small percentage of sites make their referrer logs public (either deliberately or through misconfiguration), when these are indexed, they can be used to game the search engine of the site they’re trying to boost.

Stopping the spam with mod_security

I don’t like spammers, and it was starting to make my logs (and those of my client’s) a little noisy. So, I decided to do something about it. So, using mod_security, I added a couple of simple rules, which would drop the traffic where the referrer contained certain keywords.

Simple, but effective:

SecRule REQUEST_HEADERS:Referer "^https?://(www\.)?buttons\-for\-website\.com/?" \
        "phase:1,log,deny,status:503,msg:'Referer spam'"

SecRule REQUEST_HEADERS:Referer "^https?://(www\.)?simple\-share\-buttons\.com/?" \
        "phase:1,log,deny,status:503,msg:'Referer spam'"


... etc... 

This seemed to put an end to the worst of it.

I also noticed that a few spammers were posting with obvious spam keywords in the referrer header, so I added a similar rule to block those for good measure:

SecRule REQUEST_HEADERS:Referer "(viagra|phentermine|cialis)" \
        "phase:1,log,deny,status:503,msg:'Referer spam'"

SecRule REQUEST_HEADERS:Referer "(poker|casino|holdem)" \
        "phase:1,log,deny,status:503,msg:'Referer spam'"

Testing

To test your rules, you can use curl to hit your site and send a triggering referrer, e.g.

 curl --referer https://button-for-website.com/

Or

curl --referer https://example.com/poker

Hope that helps!

I’m increasingly of the opinion, as you might have guessed from reading past articles on my blog, that if you can encrypt a thing, you must encrypt a thing, if it’s sent over the internet especially  after adding Google Analytics to WordPress.

So, since more crypto use is always a good thing, I wanted to find a way to encrypt email sent from my WordPress blog. Specifically, I wanted to encrypt my “hire me” contact form, which is emailed to me and quite often contains sensitive information. Sometimes clients are quite forthcoming in their initial messages, so I think it’s professional to protect that.

Although the contact form is the primary use case, this code should work for any email (with only one recipient) sent via wordpress’ internal code, providing the address has a valid (non expired encrypting) public key on file. Adding a key is, in this code, a manual process, however it’d be trivial to extend the code to chat to a key server.

So, anyway, you need to find the functions.php for your theme (I wanted to do this quickly, so I didn’t write a plugin), and put in the following code.

You’ll also need to install the gnupg extension for php. If you’re on debian, this should just be a matter of apt-get install php5-gnupg.

/**
Recursively find a non-expired encryption key for a given address.
*/
function find_encryption_key($keys) {

    $fingerprint = null;
    foreach ($keys as $k) {
        
        if ((!$k['expired'])  && ($k['can_encrypt']) && (!$fingerprint) && (isset($k['fingerprint']))) {
            $fingerprint = $k['fingerprint'];
        }
        
        if (!$fingerprint && isset($k['subkeys'])) {
            $fingerprint = find_encryption_key($k['subkeys']);
        }
    }
    
    return $fingerprint;
}

/**
Encrypt $message for delivery to $address
*/
function encryptto($message, $address) {
    
    $gpg = new gnupg();
    
    if (is_array($address)) {
        $address = $address[0];
    }
    
    // Find keys
    $keys = $gpg->keyinfo($address);
    if ($keys) {
        $fingerprint = find_encryption_key($keys);

        $gpg->addencryptkey($fingerprint);
        
        return $gpg->encrypt($message);
    }
    
    return false;
}

// Attempt to send encrypted email.
add_filter( 'wp_mail', function ($args) {
    $new_wp_mail = array(
        'to'          => $args['to'],
        'subject'     => $args['subject'],
        'message'     => $args['message'],
        'headers'     => $args['headers'],
        'attachments' => $args['attachments'],
    );
    
    if ($encrypt = encryptto($args['message'], $args['to'])) {
        $new_wp_mail['message'] = $encrypt;
    }
    
    return $new_wp_mail;
}, 1);

This code will try and find a key for the to address and attempt PGP encryption.

It’s not perfect, for example, if encryption fails for whatever reason, the message will be sent in the clear. I did it this way since not everyone’s public key will be on file, but I still wanted the email sent, so this is probably a good thing.

Also, for jetpack contact forms & comments at least, the code will fire the clear message text to Akismet, if you have the plugin installed. The latest version of Akismet will default to sending the message over TLS, so this isn’t the end of the world if you’re worried about passive monitoring.

Anyway, the more encrypted traffic on the net the better. Have fun!