In a previous 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.
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.
Detecting WordPress attacks
During the installation of OSSEC you choose 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. If you are using the HackerTarget.com vulnerability scanning service or your own scanning solution you need to white list the IP addresses of the scanning servers.
In a default installation of OSSEC, some common WordPress attacks will be detected and blocked automagically if in active response (IPS) mode. If you are using non-blocking mode (IDS), an email will usually be generated to alert you to the fact that an attack is underway.
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 several 404's for normal visitors you may want to confirm that legitimate visitors are not being blocked by this rule.
Alert example
Let's examine the alert below 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"
The test OSSEC installation has active response enabled, so the source IP address 192.168.1.50 is 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
. 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.
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 ( https://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 https://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; https://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; https://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; https://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; https://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; https://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; https://nmap.org/book/nse.html)"
Tuning
Without active response enabled these rules will send an email alert advising you of the attack. In recent months there was a massive increase in automated brute force attacks. This resulted in 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 not difficult to create custom rules. The following rule is added to get visibility into attackers performing reconnaissance against a WordPress installation. As seen in the Attacking WordPress article, finding the exact version of the WordPress installation is achieved by looking for the presence of the /readme.html
file.
To create a rule to detect recon against /readme.html
, 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. 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 file integrity checking. This feature detects changes to the servers file system indicating a compromise has occurred, or a problem in the system. We can also add the WordPress installation path to 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. It is also possible to 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>
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 if someone does compromise the system and adds nasty javascript
or dodgy 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/
Conclusion
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.