Defending WordPress with OSSEC

In a recent post I covered the ways a WordPress site can be attacked. Using the open source OSSEC the majority of those attacks can be detected and even blocked at the system level.

Note that using OSSEC requires you to have full control of your server, generally this means either hosting on a dedicated server or a VPS. In a shared hosting or managed WordPress environment protection at the system level is the responsibility of the hosting company.

OSSEC is a host based Intrusion Detection System (HIDS). It can also be installed as an Intrusion Prevention System (IPS) as it has the capability of blocking attacks in real time as they are detected.

During the installation of OSSEC you can chose to have the system run in IDS (passive monitoring) or IPS (active response) mode. When using the active response option real time firewall blocking will stop attackers in their tracks. Note that if you are using the HackerTarget.com vulnerability scanning service or your own scanning solution you will wish to white list the IP addresses of the scanning servers.

Detecting WordPress attacks

In a default installation of OSSEC a number of common WordPress attacks will be detected and blocked automagically if in active response (IPS) mode. If you are using non-blocking mode (IDS) then an email will usually be generated to alert you to the fact that an attack is under way.

Attackers brute forcing plugins, themes or timthumb's will find themselves blocked by the level 6 rule that detects multiple 404's. If your site is broken and does produce a number of 404's for normal visitors you may want to confirm that legitimate visitors are not being blocked by this rule.

Lets examine the alert generated by the Multiple 404 rule, this was generated by the WPScan WordPress security testing tool that is looking for readable versions of the wp-config.php file - this file contains database details including the password.

This alert was found in the /var/ossec/logs/alerts/alerts.log file.

** Alert 1383085992.19700: mail  - web,accesslog,web_scan,recon,
2013 Oct 30 09:33:12 xwing01->/var/log/apache2/access.log
Rule: 31151 (level 10) -> 'Multiple web server 400 error codes from same source ip.'
Src IP: 192.168.1.50
192.168.1.50 - - [30/Oct/2013:09:33:12 +1100] "GET /wordpress/wp-config.php.old HTTP/1.1" 404 487 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0"
192.168.1.50 - - [30/Oct/2013:09:33:12 +1100] "GET /wordpress/wp-config.old HTTP/1.1" 404 483 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0"
192.168.1.50 - - [30/Oct/2013:09:33:12 +1100] "GET /wordpress/wp-config.save HTTP/1.1" 404 484 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0"
192.168.1.50 - - [30/Oct/2013:09:33:12 +1100] "GET /wordpress/wp-config.php.bak HTTP/1.1" 404 487 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0"
192.168.1.50 - - [30/Oct/2013:09:33:12 +1100] "GET /wordpress/wp-config.bak HTTP/1.1" 404 483 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0"
192.168.1.50 - - [30/Oct/2013:09:33:12 +1100] "GET /wordpress/wp-config.php_bak HTTP/1.1" 404 487 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0"
192.168.1.50 - - [30/Oct/2013:09:33:12 +1100] "GET /wordpress/wp-config.php.swo HTTP/1.1" 404 487 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0"

On my test OSSEC installation I have active response enabled, so the source IP address 192.168.1.50 was automatically blocked with a firewall rule. Notice the above alert was from Rule 31151 that is classed as level 10. The default active response (block) level is any rule level 6 or greater, this can be found in the /var/ossec/etc/ossec.conf file.

The triggered active responses can be seen in the log /var/ossec/logs/active-responses.log, as you can see after 10 minutes (600 second default) the block rule is removed.

Wed Oct 30 09:33:12 EST 2013 /var/ossec/active-response/bin/host-deny.sh add - 192.168.1.50 1383085992.19700 31151
Wed Oct 30 09:33:12 EST 2013 /var/ossec/active-response/bin/firewall-drop.sh add - 192.168.1.50 1383085992.19700 31151
Wed Oct 30 09:43:43 EST 2013 /var/ossec/active-response/bin/host-deny.sh delete - 192.168.1.50 1383085992.19700 31151
Wed Oct 30 09:43:43 EST 2013 /var/ossec/active-response/bin/firewall-drop.sh delete - 192.168.1.50 1383085992.19700 31151

Following the testing using WPScan I tried to brute force the user account 'admin2', this valid user account was detected by enumerating with the author archives method.

Did you know that against a low end VPS an attacker can brute force a WordPress user account with around 500 passwords per minute. That is 720000 passwords per day. Without security monitoring these attacks could continue until the correct password is found!

Using the Nmap NSE WordPress password brute force script I attempted a password brute force.

root@xwing01:~# nmap -sV --script http-wordpress-brute --script-args 'http-wordpress-brute.uri=/wordpress/wp-login.php' 192.168.1.50

Starting Nmap 6.25 ( http://nmap.org ) at 2013-10-30 10:15 EST
Stats: 0:05:08 elapsed; 0 hosts completed (1 up), 1 undergoing Script Scan
NSE Timing: About 33.33% done; ETC: 10:30 (0:10:06 remaining)
Nmap scan report for 192.168.1.50
Host is up (0.0000050s latency).
Not shown: 996 closed ports
PORT     STATE SERVICE         VERSION
80/tcp   open  http            Apache httpd 2.2.22 ((Ubuntu))
| http-wordpress-brute: 
|   Accounts
|     No valid accounts found
|   Statistics
|_    Performed 244 guesses in 605 seconds, average tps: 0
443/tcp  open  ssl             SSLv3
902/tcp  open  ssl/vmware-auth VMware Authentication Daemon 1.10 (Uses VNC, SOAP)
3000/tcp open  ntop-http       Ntop web interface 4.99.3

Service detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 610.98 seconds

This attack was blocked by a rule triggered by the multiple WordPress login attempts. This default OSSEC rule will auto block (or alert) if an attacker attempts to brute force user accounts.

** Alert 1383088521.45613: - web,appsec,attack
2013 Oct 30 10:15:21 xwing01->/var/log/apache2/access.log
Rule: 31510 (level 6) -> 'WordPress wp-login.php brute force attempt.'
Src IP: 192.168.1.50
192.168.1.50 - - [30/Oct/2013:10:15:20 +1100] "POST /wordpress/wp-login.php HTTP/1.1" 200 3598 "-" "Mozilla/5.0 (compatible; Nmap Scripting Engine; http://nmap.org/book/nse.html)"
192.168.1.50 - - [30/Oct/2013:10:15:20 +1100] "POST /wordpress/wp-login.php HTTP/1.1" 200 3598 "-" "Mozilla/5.0 (compatible; Nmap Scripting Engine; http://nmap.org/book/nse.html)"
192.168.1.50 - - [30/Oct/2013:10:15:20 +1100] "POST /wordpress/wp-login.php HTTP/1.1" 200 3598 "-" "Mozilla/5.0 (compatible; Nmap Scripting Engine; http://nmap.org/book/nse.html)"
192.168.1.50 - - [30/Oct/2013:10:15:19 +1100] "POST /wordpress/wp-login.php HTTP/1.1" 200 3598 "-" "Mozilla/5.0 (compatible; Nmap Scripting Engine; http://nmap.org/book/nse.html)"
192.168.1.50 - - [30/Oct/2013:10:15:19 +1100] "POST /wordpress/wp-login.php HTTP/1.1" 200 3598 "-" "Mozilla/5.0 (compatible; Nmap Scripting Engine; http://nmap.org/book/nse.html)"
192.168.1.50 - - [30/Oct/2013:10:15:19 +1100] "POST /wordpress/wp-login.php HTTP/1.1" 200 3598 "-" "Mozilla/5.0 (compatible; Nmap Scripting Engine; http://nmap.org/book/nse.html)"

Note that without active response enabled these rules will simply send an email alert advising you of the attack. In recent months there was a massive increase in automated brute force attacks this meant a steady stream of alert emails for those monitoring their WordPress. Excessive alerts can easily be suppressed, in IDS speak this is known as tuning.

Add a new rule to OSSEC

It is a not difficult to create custom rules. The following rule can be added to get visibility into attackers performing reconnaissance against our WordPress installation. As seen in the Attacking WordPress article finding the exact version of the WordPress installation can be achieved by looking for the presence of the /readme.html file.

To create a rule to detect recon against /readme.html we can add the following to the local_rules.xml file.

<rule id="100040" level="6">
   <if_sid>31100</if_sid>
   <match>readme.html</match>
   <description>WordPress Recon - /readme.html accessed.</description>
</rule>

This rule will trigger whether you have deleted (recommended) the /readme.html or not. As the rule simply looks for a matching HTTP request in the web servers log file that has the string /readme.html.

** Alert 1383091680.53706: - local,syslog,
2013 Oct 30 11:08:00 xwing01->/var/log/apache2/access.log
Rule: 100040 (level 6) -> 'WordPress Recon - /readme.html accessed.'
Src IP: 192.168.1.50
192.168.1.50 - - [30/Oct/2013:11:07:58 +1100] "GET /wordpress/wp-admin/css/install.css?ver=20100228 HTTP/1.1" 304 210 "http://192.168.1.50/wordpress/readme.html" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.34 Safari/537.36"

File integrity checks

A powerful feature of OSSEC is the file integrity checking, this can detect changes to the servers file system that may indicate a compromise has occurred or there is a problem in the system. We can also add the WordPress installation path the directories that are checked in the file integrity monitoring.

Here is the relevant part of the /var/ossec/etc/ossec.conf file that requires changing if you wish to add the WordPress installation path to the file. Here you can also change the frequency of the checks as currently it is configured to check once every 22 hours.

 <syscheck>
    <!-- Frequency that syscheck is executed - default to every 22 hours -->
    <frequency>79200</frequency>
    
    <!-- Directories to check  (perform all possible verifications) -->
    <directories check_all="yes">/etc,/usr/bin,/usr/sbin,/var/www/wordpress</directories>
    <directories check_all="yes">/bin,/sbin</directories>

    <!-- Files/directories to ignore -->
    <ignore>/etc/mtab</ignore>
    <ignore>/etc/mnttab</ignore>
    <ignore>/etc/hosts.deny</ignore>

Note if you add the WordPress path to the file monitoring in the ossec.conf, you may need to include ignore paths for the uploads and any caching folders as these contain files that regularly change.

The benefit of adding the WordPress path to your file integrity monitoring is that if someone does compromise the system and adds some nasty javascript or dodgey PHP to any of your WordPress files, this will be detected and you will be alerted.

Default Rules for Server Monitoring

In addition to directly monitoring the WordPress application and Web server logs, having OSSEC on your host will also detect:

  • SSH brute force attempts
  • New users added to the system
  • First time user logged in
  • New services (backdoors!) listening (netstat diff)
  • lots more..... take a look at /var/ossec/rules/

One of the advantages of using the OSSEC approach is you do not need to any plugins to your WordPress installation. While there are solid security plugins available for WordPress I prefer to minimise my use of plugins of any sort in WordPress installations. Reducing the number of plugins is a simple way to reduce the size of your attackable footprint. There have even been instances where security plugins have actually introduced security vulnerabilities.

Spending 10 minutes setting up OSSEC on your server will provide you with a solid base of security monitoring. With a small investment of time and the right tools your WordPress install can be as secure as the big boys. Get OSSEC, Get Visibility.

Comments are closed.