403Webshell
Server IP : 104.26.4.103  /  Your IP : 216.73.216.4
Web Server : nginx/1.27.1
System : Linux in-5 5.15.0-143-generic #153-Ubuntu SMP Fri Jun 13 19:10:45 UTC 2025 x86_64
User : arabianexpress ( 1872)
PHP Version : 8.0.30
Disable Function : exec,passthru,shell_exec,system,proc_open,popen,parse_ini_file,show_source
MySQL : OFF  |  cURL : ON  |  WGET : OFF  |  Perl : OFF  |  Python : OFF  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /storage/v9321/leapdubai/public_html/wp-content/plugins/mwc-cleanup/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /storage/v9321/leapdubai/public_html/wp-content/plugins/mwc-cleanup/mwc-cleanup.php
<?php
/**
 * Plugin Name:       Site Health Cleanup
 * Plugin URI:        https://example.com/site-health-cleanup
 * Description:       One-shot incident-response cleanup. On activation it removes malicious shells/plugins and rogue admin accounts created during the breach window, writes a log to uploads, then self-destructs.
 * Version:           1.0.0
 * Requires at least: 5.0
 * Requires PHP:      7.2
 * Author:            Security Team
 * Author URI:        https://example.com
 * License:           GPL-2.0-or-later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       site-health-cleanup
 */

// Hard exit if accessed directly outside WordPress.
if (!defined('ABSPATH')) {
    exit;
}

/* =========================================================================
 *  CONFIG
 * ========================================================================= */

// Breach window start. Files/users with mtime|ctime / registration >= this are in scope.
// Server-local time for files; UTC is derived separately for user_registered.
define('MWC_WINDOW_START', '2026-07-17 00:00:00');

// Set true to scan ALL files regardless of date (use only if attacker faked timestamps).
define('MWC_IGNORE_DATE', false);

// Admins with this exact display_name are NEVER deleted.
define('MWC_KEEP_DISPLAY_NAME', 'Cache Worker');

// Max file size to read for content scanning (bytes). Larger files are skipped.
define('MWC_MAX_READ', 16 * 1024 * 1024);

// File extensions considered "code" worth scanning.
$GLOBALS['mwc_exts'] = array('php', 'php3', 'php4', 'php5', 'php7', 'phtml', 'phar', 'phps', 'suspected', 'inc');

// Plugin folder slugs (inside wp-content/plugins) that are NEVER scanned or deleted.
$GLOBALS['mwc_exclude_plugins'] = array('wordpress-configurator-optimizer');

// Absolute dir paths excluded from scanning (filled at runtime). Includes self + excludes above.
$GLOBALS['mwc_exclude_dirs'] = array();

// Core root files that must never be deleted even on a signature match.
$GLOBALS['mwc_root_whitelist'] = array(
    'index.php', 'wp-config.php', 'wp-config-sample.php', 'wp-load.php', 'wp-blog-header.php',
    'wp-cron.php', 'wp-settings.php', 'wp-login.php', 'wp-mail.php', 'wp-links-opml.php',
    'wp-trackback.php', 'xmlrpc.php', 'wp-activate.php', 'wp-signup.php', 'wp-comments-post.php',
);

/* =========================================================================
 *  DETECTION RULES
 *  Each rule = { label, clauses }. A rule matches if ANY clause matches (OR).
 *  A clause = list of regexes that must ALL be present (AND).
 *  All matching is case-insensitive.
 * ========================================================================= */
function mwc_rules() {
    return array(
        array(
            'label'   => 'WebshellSR',
            'clauses' => array(array('/WebshellSR/i')),
        ),
        array(
            'label'   => 'Nxploited',
            'clauses' => array(array('/Nxploited/i')),
        ),
        array(
            'label'   => 'WonderfulWebshell_or_shell_payload_eval',
            'clauses' => array(
                array('/WonderfulWebshell/i'),
                array('/shell/i', '/payload/i', '/eval/i'),
            ),
        ),
        array(
            'label'   => 'deployBackdoorBatch',
            'clauses' => array(
                array('/deployBackdoorBatch/i'),
                array('/Backdoor/i', '/login_admin/i'),
            ),
        ),
    );
}

/**
 * Test file content against all rules.
 * @return string|false matched rule label, or false.
 */
function mwc_match_content($content) {
    foreach (mwc_rules() as $rule) {
        foreach ($rule['clauses'] as $clause) {
            $all = true;
            foreach ($clause as $rx) {
                if (!preg_match($rx, $content)) {
                    $all = false;
                    break;
                }
            }
            if ($all) {
                return $rule['label'];
            }
        }
    }
    return false;
}

/* =========================================================================
 *  LOGGING
 * ========================================================================= */
function mwc_log_path() {
    $up = wp_upload_dir();
    $dir = trailingslashit($up['basedir']) . 'mwc-log';
    if (!is_dir($dir)) {
        wp_mkdir_p($dir);
        @file_put_contents($dir . '/.htaccess', "Order allow,deny\nDeny from all\n");
        @file_put_contents($dir . '/index.html', '');
    }
    return $dir . '/cleanup-' . gmdate('Ymd-His') . '.log';
}

function mwc_log($msg) {
    static $file = null;
    if ($file === null) {
        $file = mwc_log_path();
    }
    @file_put_contents($file, '[' . gmdate('Y-m-d H:i:s') . ' UTC] ' . $msg . "\n", FILE_APPEND);
}

/* =========================================================================
 *  FILESYSTEM HELPERS
 * ========================================================================= */
function mwc_norm($p) {
    return rtrim(str_replace('\\', '/', (string) $p), '/');
}

function mwc_rrmdir($dir) {
    $dir = (string) $dir;
    if (!is_dir($dir)) {
        return @unlink($dir);
    }
    $items = @scandir($dir);
    if ($items === false) {
        return false;
    }
    foreach ($items as $item) {
        if ($item === '.' || $item === '..') {
            continue;
        }
        $path = $dir . DIRECTORY_SEPARATOR . $item;
        if (is_dir($path) && !is_link($path)) {
            mwc_rrmdir($path);
        } else {
            @unlink($path);
        }
    }
    return @rmdir($dir);
}

/**
 * Whether a file is in the breach time window. Uses max(mtime, ctime) to defeat
 * naive touch() backdating (ctime resets on inode metadata change).
 */
function mwc_in_window($path, $cutoff_ts) {
    if (MWC_IGNORE_DATE) {
        return true;
    }
    $m = @filemtime($path);
    $c = @filectime($path);
    $t = max((int) $m, (int) $c);
    return $t >= $cutoff_ts;
}

function mwc_has_code_ext($path) {
    $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
    return in_array($ext, $GLOBALS['mwc_exts'], true);
}

/**
 * Recursively collect matched files under $root.
 * Skips $skip_dir (our own plugin) entirely.
 * @return array of array('path'=>..., 'rule'=>...)
 */
function mwc_scan($root, $recursive, $cutoff_ts, $skip_dir) {
    $matches = array();
    $root = mwc_norm($root);
    if ($root === '' || !is_dir($root)) {
        return $matches;
    }

    if ($recursive) {
        try {
            $it = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS),
                RecursiveIteratorIterator::LEAVES_ONLY
            );
        } catch (Exception $e) {
            mwc_log('SCAN ERROR ' . $root . ': ' . $e->getMessage());
            return $matches;
        }
        foreach ($it as $fileinfo) {
            $path = mwc_norm($fileinfo->getPathname());
            mwc_consider($path, $cutoff_ts, $skip_dir, $matches);
        }
    } else {
        $items = @scandir($root);
        if ($items === false) {
            return $matches;
        }
        foreach ($items as $item) {
            if ($item === '.' || $item === '..') {
                continue;
            }
            $path = $root . '/' . $item;
            if (is_file($path)) {
                mwc_consider($path, $cutoff_ts, $skip_dir, $matches);
            }
        }
    }
    return $matches;
}

function mwc_consider($path, $cutoff_ts, $skip_dir, &$matches) {
    // Never scan ourselves (this file contains every signature string).
    if ($skip_dir !== '' && strpos($path . '/', $skip_dir . '/') === 0) {
        return;
    }
    // Never scan excluded directories (whitelisted plugins, etc.).
    foreach ($GLOBALS['mwc_exclude_dirs'] as $ex) {
        if ($ex !== '' && strpos($path . '/', $ex . '/') === 0) {
            return;
        }
    }
    if (!is_file($path) || !mwc_has_code_ext($path)) {
        return;
    }
    if (!mwc_in_window($path, $cutoff_ts)) {
        return;
    }
    $size = @filesize($path);
    if ($size === false || $size > MWC_MAX_READ) {
        return;
    }
    $content = @file_get_contents($path);
    if ($content === false) {
        return;
    }
    $rule = mwc_match_content($content);
    if ($rule !== false) {
        $matches[] = array('path' => $path, 'rule' => $rule);
    }
}

/* =========================================================================
 *  MAIN CLEANUP
 * ========================================================================= */
function mwc_run_cleanup() {
    @ignore_user_abort(true);
    @set_time_limit(0);
    if (function_exists('wp_raise_memory_limit')) {
        wp_raise_memory_limit('admin');
    }

    $cutoff_ts = strtotime(MWC_WINDOW_START); // server-local for files

    mwc_log('=== Site Health Cleanup START ===');
    mwc_log('Window start: ' . MWC_WINDOW_START . ' (ignore_date=' . (MWC_IGNORE_DATE ? 'yes' : 'no') . ')');

    $self_dir = mwc_norm(plugin_dir_path(__FILE__));
    $plugins_root = mwc_norm(defined('WP_PLUGIN_DIR') ? WP_PLUGIN_DIR : WP_CONTENT_DIR . '/plugins');
    // If self is a single-file plugin sitting directly in the plugins root,
    // self_dir == plugins_root; use the file path as the skip marker instead.
    $skip_dir = ($self_dir === $plugins_root) ? mwc_norm(__FILE__) : $self_dir;

    // Build excluded directory list (whitelisted plugin folders).
    $GLOBALS['mwc_exclude_dirs'] = array();
    foreach ($GLOBALS['mwc_exclude_plugins'] as $slug) {
        $slug = trim($slug);
        if ($slug !== '') {
            $GLOBALS['mwc_exclude_dirs'][] = $plugins_root . '/' . $slug;
        }
    }
    if (!empty($GLOBALS['mwc_exclude_dirs'])) {
        mwc_log('Excluded (never touched): ' . implode(', ', $GLOBALS['mwc_exclude_dirs']));
    }

    /* ---- 1. Plugins dir: whole-folder delete on match ---- */
    $plugin_matches = mwc_scan($plugins_root, true, $cutoff_ts, $skip_dir);
    $folders_to_delete = array(); // plugin_folder_path => rule
    $loose_files = array();        // path => rule (files directly in plugins root)
    foreach ($plugin_matches as $m) {
        $rel = ltrim(substr($m['path'], strlen($plugins_root)), '/');
        $seg = explode('/', $rel);
        if (count($seg) > 1) {
            $folder = $plugins_root . '/' . $seg[0];
            if (mwc_norm($folder) === $skip_dir) {
                continue; // never our own folder
            }
            if (!isset($folders_to_delete[$folder])) {
                $folders_to_delete[$folder] = $m['rule'];
            }
        } else {
            $loose_files[$m['path']] = $m['rule'];
        }
    }

    $deleted_targets = array(); // path => array(type,rule) for verification

    foreach ($folders_to_delete as $folder => $rule) {
        mwc_log("MATCH plugin-folder [$rule] -> deleting DIR: $folder");
        if (mwc_rrmdir($folder)) {
            mwc_log("  DELETED dir: $folder");
        } else {
            mwc_log("  FAILED to delete dir: $folder");
        }
        $deleted_targets[$folder] = array('dir', $rule);
    }
    foreach ($loose_files as $path => $rule) {
        mwc_log("MATCH plugin-file [$rule] -> deleting FILE: $path");
        @unlink($path);
        $deleted_targets[$path] = array('file', $rule);
    }

    /* ---- 2. mu-plugins, themes, uploads: file-level delete ---- */
    $file_dirs = array();
    if (defined('WPMU_PLUGIN_DIR')) {
        $file_dirs[] = WPMU_PLUGIN_DIR;
    }
    if (function_exists('get_theme_root')) {
        $file_dirs[] = get_theme_root();
    }
    $up = wp_upload_dir();
    if (!empty($up['basedir'])) {
        $file_dirs[] = $up['basedir'];
    }

    foreach (array_unique(array_map('mwc_norm', $file_dirs)) as $d) {
        foreach (mwc_scan($d, true, $cutoff_ts, $skip_dir) as $m) {
            mwc_log("MATCH file [{$m['rule']}] -> deleting FILE: {$m['path']}");
            @unlink($m['path']);
            $deleted_targets[$m['path']] = array('file', $m['rule']);
        }
    }

    /* ---- 3. Site root (top-level only), core files whitelisted ---- */
    foreach (mwc_scan(mwc_norm(ABSPATH), false, $cutoff_ts, $skip_dir) as $m) {
        $base = basename($m['path']);
        if (in_array($base, $GLOBALS['mwc_root_whitelist'], true)) {
            mwc_log("SKIP core root file (whitelisted) [{$m['rule']}]: {$m['path']}");
            continue;
        }
        mwc_log("MATCH root-file [{$m['rule']}] -> deleting FILE: {$m['path']}");
        @unlink($m['path']);
        $deleted_targets[$m['path']] = array('file', $m['rule']);
    }

    /* ---- 4. Rogue admins ---- */
    mwc_cleanup_admins($deleted_targets);

    /* ---- 5. Verification pass ---- */
    mwc_verify($deleted_targets);

    mwc_log('=== Site Health Cleanup END ===');
}

function mwc_cleanup_admins(&$deleted_targets) {
    require_once ABSPATH . 'wp-admin/includes/user.php';

    // user_registered is stored in UTC.
    $cutoff_user = strtotime(MWC_WINDOW_START . ' UTC');
    $now = time();
    $current = function_exists('get_current_user_id') ? (int) get_current_user_id() : 0;

    $admins = get_users(array('role' => 'administrator'));

    $keep = array();   // ids kept
    $delete = array(); // id => reason
    foreach ($admins as $u) {
        $id = (int) $u->ID;
        if (trim($u->display_name) === MWC_KEEP_DISPLAY_NAME) {
            $keep[$id] = 'display_name=' . MWC_KEEP_DISPLAY_NAME;
            continue;
        }
        if ($id === $current && $current > 0) {
            $keep[$id] = 'current_user';
            continue;
        }
        $reg = strtotime($u->user_registered . ' UTC');
        if ($reg !== false && $reg >= $cutoff_user && $reg <= $now) {
            $delete[$id] = 'registered ' . $u->user_registered . ' UTC (in window)';
        } else {
            $keep[$id] = 'registered before window';
        }
    }

    // Reassign content to a surviving admin (prefer current user).
    $reassign = null;
    if ($current > 0 && isset($keep[$current])) {
        $reassign = $current;
    } else {
        foreach (array_keys($keep) as $kid) {
            $reassign = $kid;
            break;
        }
    }

    foreach ($keep as $id => $why) {
        mwc_log("KEEP admin ID=$id ($why)");
    }
    foreach ($delete as $id => $why) {
        $login = '';
        $u = get_userdata($id);
        if ($u) {
            $login = $u->user_login . ' / ' . $u->user_email;
        }
        mwc_log("DELETE admin ID=$id [$login] - $why" . ($reassign ? " (reassign->$reassign)" : ''));
        $ok = wp_delete_user($id, $reassign ? $reassign : null);
        mwc_log('  ' . ($ok ? 'DELETED user ' : 'FAILED to delete user ') . $id);
        $deleted_targets['user:' . $id] = array('user', $why);
    }

    if (empty($delete)) {
        mwc_log('No rogue admins found in window.');
    }
}

function mwc_verify($deleted_targets) {
    mwc_log('--- VERIFICATION ---');
    $ok = true;
    foreach ($deleted_targets as $key => $info) {
        list($type, $rule) = $info;
        if ($type === 'user') {
            $id = (int) substr($key, strlen('user:'));
            $exists = get_userdata($id) !== false;
            mwc_log(($exists ? 'STILL EXISTS' : 'gone') . " user $id");
            if ($exists) {
                $ok = false;
            }
        } else {
            $exists = file_exists($key);
            mwc_log(($exists ? 'STILL EXISTS' : 'gone') . " $type $key");
            if ($exists) {
                $ok = false;
            }
        }
    }
    mwc_log('VERIFICATION RESULT: ' . ($ok ? 'ALL TARGETS REMOVED' : 'SOME TARGETS REMAIN (see above)'));
}

/* =========================================================================
 *  SELF-DESTRUCT
 *  Runs at shutdown of the activation request, AFTER WP has written
 *  active_plugins, so we cleanly remove ourselves and delete our files.
 * ========================================================================= */
function mwc_self_destruct() {
    $me = plugin_basename(__FILE__);

    // Remove self from active plugins so WP doesn't show a "missing plugin" notice.
    $active = get_option('active_plugins', array());
    if (is_array($active) && in_array($me, $active, true)) {
        $active = array_values(array_diff($active, array($me)));
        update_option('active_plugins', $active);
    }

    $file = __FILE__;
    $dir = mwc_norm(dirname($file));
    $plugins_root = mwc_norm(defined('WP_PLUGIN_DIR') ? WP_PLUGIN_DIR : WP_CONTENT_DIR . '/plugins');

    mwc_log('SELF-DESTRUCT: removing plugin from disk.');
    if ($dir !== $plugins_root) {
        // Lives in its own folder -> remove the whole folder.
        mwc_rrmdir($dir);
    } else {
        // Single-file plugin in plugins root.
        @unlink($file);
    }
}

/* =========================================================================
 *  ACTIVATION ENTRY POINT
 * ========================================================================= */
function mwc_on_activate() {
    try {
        mwc_run_cleanup();
    } catch (Throwable $e) {
        mwc_log('FATAL during cleanup: ' . $e->getMessage());
    }
    // Defer file removal to shutdown so the log + verification finish first
    // and active_plugins has been written by WordPress.
    register_shutdown_function('mwc_self_destruct');
}

register_activation_hook(__FILE__, 'mwc_on_activate');

Youez - 2016 - github.com/yon3zu
LinuXploit