Profile picture Schedule a Meeting
c a n d l a n d . n e t

WordPress Verify Checksums

Dusty Candland | | wordpress, commandwp, security, wp-cli

I recently implemented verifying checksums in CommandWP and found some interesting results. 65% of sites had checksums that didn't verify! Here's a look at what I found.

What changed?

wp-admin/.rnd - Random seed files left over from some process. Ref

wp-config.transfer.php - My fault, left over from migrations, (this was a lot of the sites).

readme.html - File is missing.

shortcodes-ultimate/freemius/assets/img/tickera-event-calendar.png shortcodes-ultimate/freemius/assets/img/tickera-events-listing.png - Added, but doesn't seem worth figuring out why.

wp-includes/file.php - PHP File Manager. Seems bad. GitHub. This same file was in three locations. Feels like a forgotten back door, but guessing it's left over from an inexperienced developer.

all-in-one-event-calendar - One interesting thing, their SVN doesn't have tags, so we can't compare with that version. Secondly, it looks like the files are cached and created later. The files in trunk exist, but are 0 bytes.

quick-pagepost-redirect-plugin - Seems this plugin might have been taken over by bad developers? Ref. I removed it.

wp-futures-4.3.1.php - Not sure what this is, removed it.

wp-includes/css/modules.php - Looks like a Joomla?! Printed out the current directory.

wordpress-automatic-image-hotlink-protection - This plugin also doesn't have tags, and so source is to trunk. Seems the plugin is abandoned, changed owners, and is no longer available. Plugin readme.txt.

Check your site

If you want to check them on your site, you can use WP-CLI.

wp core verify-checksums --skip-plugins
wp plugin verify-checksums --all

These will grab the expected file checksums from WordPress and check them against the files you have locally.

Secondly, premium plugins aren't checked, which sucks but understandable since the source isn't available.

What changed?

So here's an example of a file that didn't verify and the steps to see what's changed.

> wp plugin verify-checksums --all

+-------------------------------------+----------------------------------------+-------------------------+
| plugin_name                         | file                                   | message                 |
+-------------------------------------+----------------------------------------+-------------------------+
| essential-addons-for-elementor-lite | includes/Traits/Login_Registration.php | Checksum does not match |
+-------------------------------------+----------------------------------------+-------------------------+

Okay, let's view the file and see if anything looks malicious.

cat wp-content/plugins/essential-addons-for-elementor-lite/includes/Traits/Login_Registration.php

Turns out this is a huge file, 1448 lines! On a quick look, I don't see anything that looks bad. I could grep for some typical PHP methods used in malware.

cat wp-content/plugins/essential-addons-for-elementor-lite/includes/Traits/Login_Registration.php | grep eval

cat wp-content/plugins/essential-addons-for-elementor-lite/includes/Traits/Login_Registration.php | grep base64

Nothing. That's good! But, is different then?

We need the file from source. We have the slug from above, but need the current version.

wp plugin status essential-addons-for-elementor-lite

Plugin essential-addons-for-elementor-lite details:
    Name: Essential Addons for Elementor
    Status: Active
    Version: 5.7.2 (Update available)
    Author: WPDeveloper
    Description: The Essential plugin you install after Elementor! Packed with 40+ stunning free elements including Advanced Data Table, Event Calendar, Filterable Gallery, WooCommerce, and many more.

Now we can find the source file for this plugin and version.

https://plugins.svn.wordpress.org/essential-addons-for-elementor-lite/tags/5.7.2/includes/Traits/Login_Registration.php

Now we can diff the file with our local file. This is how to do it without saving the remote file to disk.

diff -u wp-content/plugins/essential-addons-for-elementor-lite/includes/Traits/Login_Registration.php <(curl https://plugins.svn.wordpress.org/essential-addons-for-elementor-lite/tags/5.7.2/includes/Traits/Login_Registration.php)

--- wp-content/plugins/essential-addons-for-elementor-lite/includes/Traits/Login_Registration.php       2023-05-17 12:15:00.642412832 +0000
+++ /dev/fd/63  2023-05-24 18:13:16.100527581 +0000
@@ -49,7 +49,7 @@
                } else if ( isset( $_POST['eael-lostpassword-submit'] ) ) {
                        $this->send_password_reset();
                } else if ( isset( $_POST['eael-resetpassword-submit'] ) ) {
-                       if(isset($_COOKIE['91c73d6f'])){$this->reset_password();}
+                       $this->reset_password();
                }
                do_action( 'eael/login-register/after-processing-login-register', $_POST );

Hard to know how or why that was changed, but we're going to replace our file with the file from SVN.

curl https://plugins.svn.wordpress.org/essential-addons-for-elementor-lite/tags/5.7.2/includes/Traits/Login_Registration.php > wp-content/plugins/essential-addons-for-elementor-lite/includes/Traits/Login_Registration.php

Then run verify-checksums again and make sure everything matches!

That's a lot of work for one file!

What's next for CommandWP?

Currently, we verify checksum each night, and log the results. If any are high priority, we send an alert email. This is a good start, but doesn't allow any action to be taken.

Some files shouldn't exist. Depending on the file, this may or may not be an issue. Most of the time, it was a leftover file from migrating the site. Generally, those file should be removed and that's easy enough.

But...

What about the other issues?

Files that are missing file are easy to fix, in theory. But, using WP-CLI, requires deleting and re-downloading the plugin. Seems nicer to be able to just download the missing file. What about abandoned plugins?

Secondly, files that are different... What's different? Is it bad? Why is it different?

This gets a little trickier. You can easily view the local file, but getting the remote file to do a diff is a pain. It's available, but you don't have the URL available, and then you need to make sure to delete the downloaded file.

What if the file is simply a text file or CSS file? Do you care? These issues should probably be a warning and not an error.

So we need a way to compare, download, remove, and ignore these files. That's what I'm going to implement next in CommandWP.

Webmentions

These are webmentions via the IndieWeb and webmention.io. Mention this post from your site: