September 20, 2019
Cleaning Up Wordpress: Lessons Learned in Website Security
You've been working on a project for months, you've handed the keys over to the client, and then you get that dreaded email...
... on page [x] some users have been experiencing weird advertisements and popups. I think the site has been hacked! I don't know what to do. Please help! ...
I faced this situation recently. I volunteered to finish up a project for a nonprofit organization (not the one where I work 9 -5). They were one year into a new site build when the developer they originally hired ghosted them.
I'm a JavaScript guy, so I wasn't all that familiar with the entirety of the Wordpress
ecosystem, but I figured I knew enough, I had been building my own plugin for work to inject a React
application via a shortcode, thinking, "How hard can it be? It's not even real development..." (don't get offended, just expressing what I was thinking at the time).
I was very aware of security issues in a Node.js
environment, but I had always relied on other services to take care of hosting security. I had heard of .htaccess
files, but I'd never put my own LAMP stack application into production from scratch. I hadn't even given it any thought since I have been focusing more and on tech like React
, Gatsby
, React Native
, and even a little Angular
.
As easy (in terms of difficulty) it was to handle all the front-end requests of getting the site finished to the client's expectation, I took for granted my own ignorance of the rest of what was involved. I want to share what I have learned over the past two weeks so that (1) I won't forget next time, and (2) so you can avoid many of the mistakes I made.
I am going to try and provide links to all the sources that have helped my in my recent journeys and to briefly talk through some of the more important parts. Let's call this:
Wordpress Security 101
-
Always Hire a Developer You Either Know or Can Verifiably Trust
It is very tempting to hire the cheapest developer you can find. But sometimes you get what you pay for.
I like that there are sites where developers can be rated, or public reviews left, like Upwork or LinkedIn (want to suggest others?). These help provide accountability for the developer and some sense of security for the client. In this particular case, the original developer took a low fee for a quite complex site and cut a bunch of corners.
Links:
-
Never download and use unlocked or
nulled
themes - pay the regular fee from a reputable theme marketplace. Otherwise, you may install Malware!!!I didn't even question the use of the theme on the client's site. Knowing the client, I assumed they would have purchased the theme themselves but it turns out they left it to the developer, who perhaps out of ignorance or a willingness to cut corners downloaded a theme online that was corrupted with the
wp_vcd
malware.I only discovered how long the malware had been there by comparing the current site with versions of the codebase that existed while the site was offline or in maintenance mode.
This particular Malware was found within
functions.php
, and it created three files withinwp-includes
:wp-feed.php
,wp-vcd.php
,wp-tmp.php
. It wrapped pages with malvertising scripts and also added a script that logged user's IP addresses who had permissions to edit the site and systematically ignored those users, so they would never detect the malicious behavior when browsing the site themselves, whether logged in or not, whether browsing privately or not.After logging into the remote server as an administrator-level user, I found the odd looking files at first within the
wp-includes
folder. I removed them, thinking I had solved the problem. The Malware was creating redirects to sites with theoclaserver
domain and logging IP addresses towp-feed.php
. I used the following command-line script to find these files.grep -Ril "oclaserver"
grep
searches files for lines matching a given pattern.R
makes the search recursive, looking through all files and directories within the current directori
ignores casel
suppresses the output to only list the filename
Initially, I though this solved the problem, but the next time I checked, the files where back. I deleted the files, then checked again, immediately, the files were once again there, full of the same content. This means either my
wpdb
was corrupted, or some other file that gets called repeatedly by Wordpress was corrupted, or possibly both.What did I do next?
Before scouring through my DB in
phpMyAdmin
, I first identified and deleted all unused themes and plugins. The files kept appearing. So I started searching for terms within the files.What found me the culprit was that within
wp-temp
the code was setting a password variable. My only thought is that this was a hash of the admin password (so I made a note to change the admin password after cleaning up). I ran the samegrep
command but searched instead for that password value.Lo and behold, the
functions.php
of the parent theme lit up. There, I found a bunch of hacky code that was wrapping all the pages with the malicious script and systematically ignoring the IPs where an admin user had been logged in.After deleting the code, I deleted the malicious files from within
wp-includes
and the files didn't return. I wasn't yet certain the Malware was completely gone because I noticed that the code inwp-temp
was creating a cookie on the client.Before changing the admin password, I reset the
SALT Keys
within thewp-config.php
file using https://api.wordpress.org/secret-key/1.1/salt/.Then, I cleared the cache and cookies from my browser, and logged into the
wp-admin
of the site from an incognito window. Only then did I reset the admin password.Finally, I created a new admin user for myself and encouraged the client to add 2FA for their login via the Google Authenticator Plugin.
Links:
- https://www.getastra.com/blog/911/how-to-fix-wp-vcd-backdoor-hack-in-wordpress-functions-php/
- https://stackoverflow.com/questions/46219263/php-code-in-functions-php-of-all-wordpress-websites-on-my-shared-hosting
- https://labs.sucuri.net/wp-vcd-malware-comes-with-nulled-themes/
- https://blog.sucuri.net/2016/05/nulled-wordpress-themes-malvertising-black-hat-seo.html
-
On your server, add a new user and remove root login to make it harder for someone to hack your server.
Since the client already had their site installed and running on Ubuntu 18.04 a Digital Ocean droplet, the previous developer had
ssh
access to the droplet. I really didn't want them to be able to log back in, whether or not they had nefarious intent. I noticed there was only one root user on the system, so I created a new user for myself:sudo adduser myuniqueusername
I then added my new user to the
sudo
user group:sudo usermod -aG sudo myuniqueusername
And then I added administrative privileges to
myuniqueusername
:sudo visudo
Within
/etc/sudoers
:myuniqueusername ALL=(ALL:ALL) ALL
Finally, within
/etc/ssh/sshd_config
:PermitRootLogin no
Then from command-line, restart
ssh
:sudo service ssh restart
From there, do whatever is necessary to create
ssh
keys for your new account, set those up, logout, and thenssh
back in as you. -
Harden file permissions on the server to prevent unwarranted changes. From within the root directory of your Wordpress installation (perhaps
/var/www/html/
) do the following:Find what groups your user and your server belongs to and make sure that your user has ownership of all your Wordpress files.
sudo groups sudo usermod -aG groupname myuniqueusername sudo find . -exec chown myuniqueusername:groupname {} +
Change files to only be writable by owner (you), and only readable by others, Change directories to be only be created, modified, or deleted by you or Wordpress, and prevent anyone other than you or Wordpress from reading or writing to
wp-config
.sudo find . -type f -exec chmod 664 {} + sudo find . -type d -exec chmod 775 {} + sudo chmod 660 wp-config.php
Links:
-
Add hardening to your
httpd.conf
Apache configuration by disabling Server banners, hardening your.htaccess
file, by disabling directory listing, disabling HTTP Trace, preventing MIME sniffing, preventing clickjacking, preventing some forms of cross-site scripting, and securing your cookies withinwp-config.php
.Within
/etc/apache2/httpd.conf
or/etc/apache2/apache2.conf
:ServerSignature Off ServerTokens Prod
Then from command-line:
sudo service apache2 restart
Within
.htaccess
, insert between# BEGIN Wordpress
and# END Wordpress
:Options -Indexes RewriteEngine On RewriteCond %{REQUEST_METHOD} ^TRACE RewriteRule .* - [F] Header set X-Content-Type-Options nosniff Header always append X-Frame-Options SAMEORIGIN Header set X-XSS-Protection "1; mode=block"
Within
wp-config.php
, add to the end:@ini_set('session.cookie_httponly', true); @ini_set('session.cookie_secure', true); @ini_set('session.use_only_cookies', true);
There is some discussion over whether or not the above method is best, you could and perhaps should also add cookie hardening to your apache server configuration, see links below.
Links:
-
Add Security Plugins like
Sucuri Sanner
andLimit Login Attempts
.Links:
Final Thoughts
The list above is by no means exhaustive, and it's just based on things I've been learning the past couple of weeks. I am very open to correction, addition, or subtraction, from the above by anyone with more experience. While I don't plan on focusing primarily on Wordpress, I'm learning more and more LA
of the LAMP
stack through this experience, and at the end of the day, with my interest in Gatsby
, I probably will be involved more and more with the ecosystem.
That being said, there is nothing wrong with learning more about website security.
Even if static sites are the future, keeping our CMS and web-servers safe should not become lost or remain assumed knowledge.
Written by Wesley L. Handy who lives and works in Virginia Beach, VA, finding ways to build cool stuff in Gatsby and React. You should follow him on Twitter