What is PolKit?
Overview
PolKit (previously PolicyKit) is an application framework that works as a mediator between the privileged system context and the unprivileged user session. PolKit is queried whenever a process from the user session seeks to perform an action in the system context. The answer could be yes, no, or requires authentication depending on its setup, which is stated in a so-called policy. Unlike traditional privilege authorization applications like sudo, PolKit only grants root access to the activity in issue, not the entire session.
Location: /usr/bin/pkexec
PolKit Commands
- pkaction: Get information about a specific action.
- pkcheck: determines whether a process provided by —process or —system-bus-name is permitted.
- pkexec: Allows an authorized user to run an application in the role of another user.
- pkttyagent: If a desktop environment does not have its own authentication agent, this one is utilized.
Also Read: Windows Management Instrumentation Attacks – Detection & Response
PolKit Working Anatomy
- Every request via inter-process communication from unprivileged process are treated as untrusted by privilege process.
- The privilege process should take decision whether to reject or authorize the unprivilege process request.
- With the help of PolKit’s API, privilege process will decide that to authorize the unprivilege process request or not.
- The polkit authority is implemented as polkitd, a system daemon with limited privileges because it runs as the polkitd system user.
- PolKit has 2 components: Actions and Rules
- Actions are defined by applications, located under “usr/share/polkit-1/actions” and it’s extension is “.policy”
- Authorization Rules give vendors, sites, and system administrators control over authorization policies, located under “/usr/share/polkit-1/rules.d” and it’s extension is “.rules”
- Once the decision is taken by privilege process based on polkit’s policy and rules, via system bus Authentication Agent will get popup in the user session where user needs to prove that they are authorized to perform the task by giving passphrase.
Note:
Users can get temporary authorization through polkit by authenticating with either an administrator user or the owner of the session to which the client belongs. This is useful in instances where a mechanism must verify that the system’s operator is indeed the user or an administrative user.
PolKit before and after exploit
Vulnerability and Exploit details are given by Qualys Vulnerability Research Team https://blog.qualys.com/vulnerabilities-threat-research/2022/01/25/pwnkit-local-privilege-escalation-vulnerability-discovered-in-polkits-pkexec-cve-2021-4034
Before Exploit
Polkit is a Linux authorization system component. Polkit can be used to determine whether you have the required permissions when you want to conduct an activity that needs a higher level of rights. It is significantly more configurable than the classic sudo system because it relates to systemd. It is also referred to as the “sudo of systemd” because it offers a granular system for assigning permissions to users. We may use the pkexec utility to interact with polkit; it is this application that includes the Pwnkit vulnerability. Attempting to run the bash command with pkexec in a CLI session, for example, results in a pop-up asking for credentials:
After Exploit
Prior to the patch, versions of pkexec did not properly accept command-line parameters, resulting in an “out-of-bounds write” vulnerability that allows an attacker to modify the environment in which pkexec is performed.
Also Read: Osquery for Cyber Threat Detection & Incident Response
More specifically, pkexec uses a for-loop to parse any command-line arguments we pass it, starting at index 1 to offset the program’s name and obtain the first real argument, if we entered pkexec bash, the first real argument would be argument 0 — the actual command-line arguments start at index 1. Because the program’s name has no bearing on argument processing, the indexing is simply offset to disregard it.
So, if we don’t offer any counterarguments the index will always be 1
The pseudocode below may assist you in understanding this:
for (n=1; n < number of arguments; n++)
n is always fewer than the number of arguments if the number of arguments is 0. As a result, n remains at one and the loop is entirely skipped. When pkexec tries to write to the value of the argument at index n, this causes an issue. There is no argument at index n because there are no command-line arguments; instead, when the program is called using the C function execve(), the program overwrites the next thing in memory, which happens to be the first value in the list of environments variables. In other words, we may force pkexec to replace an environment variable instead by feeding it a null list of arguments.
PolKit Detection Rules
Splunk Query:
(index="linux" (((CommandLine="*GCONV_PATH=*" OR CommandLine="*pwnkit*" OR CommandLine="*gconv-modules*") OR ((Image="*pkexec") (User="*root*"))) OR (CommandLine="*gcc*" CommandLine="*-o*" CommandLine="*-shared*" CommandLine="*-fPIC*") OR (ParentImage="*pkexec")))
Elastic Query:
((process.command_line:(*GCONV_PATH\=* OR *pwnkit* OR *gconv\-modules*) OR (process.executable:*pkexec AND user.name:*root*)) OR (process.command_line:*gcc* AND process.command_line:*\-o* AND process.command_line:*\-shared* AND process.command_line:*\-fPIC*) OR process.parent.executable:*pkexec)
Elastic Rule
[metadata]
creation_date = "2022/01/26"
maturity = "production"
updated_date = "2022/01/26"
[rule]
author = ["Elastic"]
description = """
Identifies attempt to exploit a local privilege escalation in polkit pkexec (CVE-2021-4034) via unsecure environment
variable injection. Successful exploitation allows an unprivileged user to escalate to the root user.
"""
from = "now-9m"
index = ["logs-endpoint.events.*"]
language = "eql"
license = "Elastic License v2"
name = "Potential Privilege Escalation via PKEXEC"
references = ["https://seclists.org/oss-sec/2022/q1/80", "https://haxx.in/files/blasty-vs-pkexec.c"]
risk_score = 73
rule_id = "8da41fc9-7735-4b24-9cc6-c78dfc9fc9c9"
severity = "high"
tags = ["Elastic", "Host", "Linux", "Threat Detection", "Privilege Escalation"]
timestamp_override = "event.ingested"
type = "eql"
query = '''
file where file.path : "/*GCONV_PATH=*"
'''
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
name = "Exploitation for Privilege Escalation"
reference = "https://attack.mitre.org/techniques/T1068/"
id = "T1068"
[rule.threat.tactic]
name = "Privilege Escalation"
reference = "https://attack.mitre.org/tactics/TA0004/"
id = "TA0004"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
name = "Hijack Execution Flow"
reference = "https://attack.mitre.org/techniques/T1574/"
id = "T1574"
[[rule.threat.technique.subtechnique]]
name = "Path Interception by PATH Environment Variable"
reference = "https://attack.mitre.org/techniques/T1574/007/"
id = "T1574.007"
[rule.threat.tactic]
name = "Defense Evasion"
reference = "https://attack.mitre.org/tactics/TA0005/"
id = "TA0005"
QRadar Query:
SELECT UTF8(payload) from events where LOGSOURCENAME(logsourceid) ilike '%linux%' and (("Process CommandLine" ilike '%GCONV_PATH=%' or "Process CommandLine" ilike '%pwnkit%' or "Process CommandLine" ilike '%gconv-modules%') or (("Image" ilike '%pkexec') and ("username" ilike '%root%'))) or ("Process CommandLine" ilike '%gcc%' and "Process CommandLine" ilike '%-o%' and "Process CommandLine" ilike '%-shared%' and "Process CommandLine" ilike '%-fPIC%') or ("ParentImage" ilike '%pkexec')
MDATP Query:
DeviceProcessEvents | where (((ProcessCommandLine contains "GCONV_PATH=" or ProcessCommandLine contains "pwnkit" or ProcessCommandLine contains "gconv-modules") or ((FolderPath endswith "pkexec") and ((AccountName contains "root") or (AccountUpn contains "root")))) or (ProcessCommandLine contains "gcc" and ProcessCommandLine contains "-o" and ProcessCommandLine contains "-shared" and ProcessCommandLine contains "-fPIC") or (InitiatingProcessFolderPath endswith "pkexec"))
KQL Rule:
let = timewindow = 5min;
let initial_event =
LinuxAuditLog_CL
| where TimeGenerated > ago(timewindow)
| where RecordType_s == 'PROCTITLE'
| where proctitle_s == '(null)';
LinuxAuditLog_CL
| where TimeGenerated > ago(timewindow)
| where RecordType_s == 'SYSCALL' and comm_s == 'pkexec' and exe_s == '/usr/bin/pkexec'
| join kind=inner initial_event on SerialNumber_s
CrowdStrike Query
index=main sourcetype=ProcessRollup2* event_simpleName=ProcessRollup2 event_platform=Lin
| search ParentBaseFileName=pkexec AND UID_decimal=0
| stats values(CommandLine) as CommandLine, count(aid) as executionCount by aid, ComputerName, ParentBaseFileName, FileName, UID_decimal
| sort + executionCount
Velociraptor Query
name: Linux.Detection.CVE20214034
description: |
This artifact lists processes running as root that were spawns by processes that are not
running as root. This kind of behavior is normal for things like sudo or su but for other
processes (especially /bin/bash) it could represent a process launched via CVE-2021-4034.
The artifact looks for running processes with this property as well as search the auth
log files for evidence of past execution of this exploit.
type: CLIENT
parameters:
- name: AcceptableParentExeRegex
description: A list of acceptable parent processes that are OK (unset to see all parents)
type: regex
default: ^(/usr/bin/sudo)
- name: AuthLogsGlob
default: /var/log/auth.log*
precondition:
SELECT OS From info() where OS = 'linux'
sources:
- query: |
SELECT Pid, Ppid, Cmdline, Exe, Uids, Username, {
SELECT Pid, Cmdline, Exe, Uids, Username
FROM pslist(pid=Ppid)
} AS Parent
FROM pslist()
WHERE Ppid
AND Username =~ "root"
AND Parent.Username != Username
AND if(condition=AcceptableParentExeRegex,
then=NOT Parent.Exe =~ AcceptableParentExeRegex,
else=TRUE)
- name: AuthLogs
query: |
SELECT * FROM foreach(row={
SELECT * FROM glob(globs=AuthLogsGlob)
}, query={
SELECT * FROM parse_lines(filename=FullPath)
WHERE Line =~ "pkexec.+The value for environment variable XAUTHORITY contains suscipious content"
})
Mitigations or Workaround
Patch Release
- Red Hat – https://access.redhat.com/security/cve/CVE-2021-4034
- Ubuntu – https://ubuntu.com/security/CVE-2021-4034
- CloudLinux OS 7, 7 hybrid, and 8 – https://blog.cloudlinux.com/cve-2021-4034-cloudlinux-os-7-7-hybrid-and-8-updated
- Debian – https://security-tracker.debian.org/tracker/CVE-2021-4034
- SUSE – https://www.suse.com/security/cve/CVE-2021-4034.html
- CentOS – https://www.linuxcompatible.org/story/cesa20220274-important-centos-7-polkit-security-update/
Also Read: Threat Hunting using DNS logs – Soc Incident Response Procedure
Temporary workaround
Simply removing the SUID bit from the pkexec binary is the suggested hotfix. This can be accomplished with a command like this:
sudo chmod 0755 /usr/bin/pkexec
It’s worth noting that the Pwnkit exploit comes in a variety of flavors, each of which uses distinct environment variables and exploits the vulnerability in different ways. Some leave traces and logs behind, while others do not. You can test whether a machine has been patched by running a copy of the exploit against it. The system gets fixed if the exploit returns the pkexec help menu.
References
- https://documentation.suse.com/sles/15-SP2/html/SLES-all/cha-security-policykit.html#sec-security-policykit-query
- https://www.freedesktop.org/software/polkit/docs/latest/polkit.8.html
- https://tryhackme.com/room/pwnkit
- https://www.reddit.com/r/crowdstrike/comments/sdfeig/20220126_cool_query_friday_hunting_pwnkit_local/?utm_medium=android_app&utm_source=share
- https://socprime.com/blog/detect-cve-2021-4034-a-notorious-pwnkit-vulnerability-affecting-all-major-linux-distros/
- https://docs.velociraptor.app/exchange/artifacts/pages/linux.detection.cve20214034/
- https://github.com/zakibro/auditd-detections/blob/main/Detections/cve_2021_4034.md
- https://github.com/elastic/detection-rules/blob/24df772b484c6f5ad94b9d045aedd899372b34e5/rules/linux/privilege_escalation_pkexec_envar_hijack.toml