AlkantarClanX12

Your IP : 216.73.217.24


Current Path : /www/capitalgmcbuickregina_830/public/wp-content/mu-plugins/
Upload File :
Current File : /www/capitalgmcbuickregina_830/public/wp-content/mu-plugins/lbx-block-user-enum.php

<?php
/**
 * Plugin Name: LBX Security Defense
 * Description: Blocks unauthenticated /wp-json/wp/v2/users enumeration, ?author=N redirects, oembed author leaks, and known attacker IPs from the April 2026 campaign. Deployed fleet-wide as response to CVE-2026-0740 aftermath.
 * Version: 2.1.0
 * Author: Leadbox Security
 */

if (!defined('ABSPATH')) exit;

/**
 * 0. Deny known attacker IPs at the earliest possible moment.
 *    These IPs were observed actively exploiting CVE-2026-0740 and/or
 *    performing user enumeration reconnaissance against the Leadbox fleet.
 */
$lbx_blocked_ips = [
    // User enumeration wave (2026-04-09)
    '62.60.131.158',
    '62.60.131.175',
    // Indonesian credential-stuffing wave (2026-04-12)
    '180.252.169.1',
    '36.37.209.184',
    // Chinese webshell + wp-file-manager abuse (2026-04-24, capitalgmcbuickregina)
    '23.142.200.55',
    // CVE-2026-0740 exploitation wave (2026-04-07/08)
    '109.107.178.102',
    '176.213.124.119',
    '46.246.126.191',
    '185.36.143.39',
    '192.36.61.172',
    '194.31.175.187',
    '18.182.54.150',
];

$lbx_client_ip = null;
foreach (['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'REMOTE_ADDR'] as $h) {
    if (!empty($_SERVER[$h])) {
        $candidate = explode(',', $_SERVER[$h])[0];
        $lbx_client_ip = trim($candidate);
        break;
    }
}

// Stage 1 (pre-WP): check hardcoded list immediately
if ($lbx_client_ip && in_array($lbx_client_ip, $lbx_blocked_ips, true)) {
    header('HTTP/1.1 403 Forbidden');
    header('Content-Type: text/plain; charset=utf-8');
    header('X-Robots-Tag: noindex, nofollow');
    echo "403 Forbidden";
    exit;
}

/**
 * 1. Unregister /wp/v2/users endpoint entirely for unauthenticated requests.
 */
add_filter('rest_endpoints', function ($endpoints) {
    if (is_user_logged_in()) {
        return $endpoints;
    }
    if (isset($endpoints['/wp/v2/users'])) {
        unset($endpoints['/wp/v2/users']);
    }
    if (isset($endpoints['/wp/v2/users/(?P<id>[\d]+)'])) {
        unset($endpoints['/wp/v2/users/(?P<id>[\d]+)']);
    }
    return $endpoints;
});

/**
 * 2. Belt-and-suspenders REST rejection for /users/*.
 */
add_filter('rest_pre_dispatch', function ($result, $server, $request) {
    $route = $request->get_route();
    if (strpos($route, '/wp/v2/users') === 0 && !is_user_logged_in()) {
        return new WP_Error(
            'rest_forbidden',
            'User listing is not exposed.',
            ['status' => 401]
        );
    }
    return $result;
}, 10, 3);

/**
 * 3. Block ?author=N enumeration.
 */
add_action('template_redirect', function () {
    if (is_author() && !is_user_logged_in()) {
        wp_safe_redirect(home_url('/'), 301);
        exit;
    }
});

/**
 * 4. Strip author info from oEmbed responses.
 */
add_filter('oembed_response_data', function ($data) {
    unset($data['author_name'], $data['author_url']);
    return $data;
});

/**
 * 5. Remove author info from RSS feeds.
 */
add_filter('the_author', function ($author) {
    if (is_feed()) {
        return get_bloginfo('name');
    }
    return $author;
});

/**
 * 6. Block user sitemap (wp-sitemap-users-1.xml).
 *    WordPress 5.5+ auto-generates this, leaking all author slugs.
 */
add_filter('wp_sitemaps_add_provider', function ($provider, $name) {
    if ($name === 'users') {
        return false;
    }
    return $provider;
}, 10, 2);

/**
 * 7. Strip author info from posts/pages REST responses (unauthenticated).
 *    Prevents username harvesting via /wp-json/wp/v2/posts?_embed
 */
function lbx_strip_author_from_rest($response, $post, $request) {
    if (is_user_logged_in()) {
        return $response;
    }
    $data = $response->get_data();
    if (isset($data['author'])) {
        $data['author'] = 0;
    }
    $response->set_data($data);
    $response->remove_link('author');
    return $response;
}
add_filter('rest_prepare_post', 'lbx_strip_author_from_rest', 10, 3);
add_filter('rest_prepare_page', 'lbx_strip_author_from_rest', 10, 3);

/**
 * 8. Stage 2 IP check: merge hardcoded + configurable blocklist after WP loads.
 *    Allows adding IPs without redeploying via:
 *    wp option patch insert lbx_blocked_ips '' '1.2.3.4'
 */
add_action('muplugins_loaded', function () {
    global $lbx_client_ip, $lbx_blocked_ips;
    if (!$lbx_client_ip) return;

    $db_ips = get_option('lbx_blocked_ips', []);
    if (!is_array($db_ips)) $db_ips = [];
    $all_ips = array_unique(array_merge($lbx_blocked_ips, $db_ips));

    if (in_array($lbx_client_ip, $all_ips, true) && !in_array($lbx_client_ip, $lbx_blocked_ips, true)) {
        // This IP was in the DB list but not the hardcoded list — block it now
        header('HTTP/1.1 403 Forbidden');
        header('Content-Type: text/plain; charset=utf-8');
        echo "403 Forbidden";
        exit;
    }
});

/**
 * Helper: get full merged blocklist (called by lbx-login-monitor.php).
 */
function lbx_get_full_blocklist() {
    global $lbx_blocked_ips;
    $db_ips = get_option('lbx_blocked_ips', []);
    if (!is_array($db_ips)) $db_ips = [];
    return array_unique(array_merge($lbx_blocked_ips, $db_ips));
}