Finding and removing malware from a WordPress site

Hosts will occasionally tell us that a site has been compromised and that suspect files have been quarantined. The heads-up is helpful, but they often don’t find all the compromised files.

What to look for

Within the WordPress install –

  • Files with odd names like ajlskjna.php. WordPress files normally start with wp-, eg. wp-config.php. Plugin files normally start with a prefix like woo- for Woocommerce. Files without a prefix are suspect.
  • PHP files where there should only be js or css files. Inside directories labelled “js”, “css”, “styles” or “scripts”, you can expect to only find javascript (.js) and CSS (.css) files. .php files in those locations are suspect.
  • File with extensions that look like .txt.php or .php.txt are suspect.
  • PHP files where there should only be images. The /uploads directory in wp-content contains subfolder labelled, eg. 2019/02 (for Feb 2019). Files in the year/month folders should only be jpg, png, pdf, svg, or other image formats. PHP files located there are suspect.
  • Code containing the method names “base64” or “eval”. You’ll see something like this:
base64_encode(base_64_decode( { here you’ll see a long block of text with no tabs, spaces or line breaks in it });`
‘eval( { another long block of text might be here } );

These files are suspect.

  • Big blocks of code with no linebreaks, tabs or spaces appended to the beginning or end of a file that otherwise looks normal.

There may be many malicious files scattered through the install. These steps help to remove the damage with as little physical inspection as possible.

You might be tempted to restore the site from a backup, but don’t do that unless you are 100% sure you know when the malware arrived. Sites that have been neglected may have been subjected to more than one attack at different times.

  1. Back up the database using cPanel or FTP if possible. Don’t use Akeeba if there is malware; you’ll be backing up malware.
  2. Download a fresh copy of WordPress from wordpress.org.
  3. Rename the wp-includes and wp-admin directory on the server.
  4. Upload fresh copies of wp-admin and wp-includes as well as all WordPress files in the root directory.
  5. Don’t overwrite wp-config.php, but manually inspect it for suspicious code.
  6. Delete any suspicious .php files from the top level directory.
  7. Delete any unused themes (don’t accidentally delete the parent theme if a child theme is active).
  8. Delete any inactive plugins.
  9. Delete the renamed wp-admin and wp-includes directories.
  10. Go into the site’s dashboard; if any plugins need updates, perform the updates. This will replace the plugin with a fresh copy, making it safe.
  11. Any plugins that weren’t updated, download to your local machine. Also download the theme.
  12. Use a directory find to search for base64 or eval. If you find any and you’re not sure what to do, ask for help.
  13. Inspect the uploads directory for .php files and delete any that are suspect. If you’re not sure, ask.
  14. Change the site’s FTP password and admin password.
Advertisements

Post Status Transitions

If someone needs to be notified about posts when they are submitted, published, reviewed or whatever else, you can use the Post Status Transition hooks.

https://codex.wordpress.org/Post_Status_Transitions

I recently used these to create a small plugin to notify employers that their job posting was submitted through the WP Job Manager plugin. I may make a pull request at some point in the future as well, it would be a handy bit of functionality.

define( ‘WP_DEBUG’, true );

define( ‘WP_DEBUG_DISPLAY’, false );
define( ‘WP_DEBUG_LOG’, true );

phpcs . –standard=WordPress-VIP

phpcs . –standard=WordPress-VIP

Luke Edward says,

Curious — how do you all set up your local debug and logging settings? I highly recommend:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_DISPLAY', false );
define( 'WP_DEBUG_LOG', true );
define( 'SCRIPT_DEBUG', false );

On my Mac I open `<site> /wp-content/debug.log` in the built-in *Console* app and watch the log. I can also use `error_log()` statements to throw data into the log instead of full step-debugging or var_dumps to the screen. (`error_log(var_export( $var, true));` can be nice)

I was fighting with gulp-compass.

If you get this error,

Error in plugin 'gulp-compass'
Message:
    You need to have Ruby and Compass installed and in your system PATH for this task to work.

you have to install gulp-compass. The command is this:

gem install compass

but I got this error.

Building native extensions.  This could take a while...
ERROR:  Error installing compass:
	ERROR: Failed to build gem native extension.

    current directory: /Library/Ruby/Gems/2.3.0/gems/ffi-1.9.23/ext/ffi_c
/System/Library/Frameworks/Ruby.framework/Versions/2.3/usr/bin/ruby -r ./siteconf20180322-26786-1l64sic.rb extconf.rb
mkmf.rb can't find header files for ruby at /System/Library/Frameworks/Ruby.framework/Versions/2.3/usr/lib/ruby/include/ruby.h

extconf failed, exit code 1

Gem files will remain installed in /Library/Ruby/Gems/2.3.0/gems/ffi-1.9.23 for inspection.
Results logged to /Library/Ruby/Gems/2.3.0/extensions/universal-darwin-17/2.3.0/ffi-1.9.23/gem_make.out
xcode-select --install

fixed the problem.

Seen this error more than once now –

>> Error: Node Sass does not yet support your current environment: OS X 64-bit with Unsupported runtime (57)
>> For more information on which environments are supported please see:
>> https://github.com/sass/node-sass/releases/tag/v3.13.1
Warning: Task "sass" not found. Use --force to continue.

Some answers found here. https://github.com/sass/node-sass/issues/1764

tld;dr: try removing node_modules and running npm install.

npm rebuild node-sass

also helps.

gem install scss-lint

maybe as well.