A locksmith's paperwork jam that only the building owner can trigger
polkit (formerly PolicyKit) parses XML .policy files under /usr/share/polkit-1/actions/ and /etc/polkit-1/rules.d/ to decide who can perform privileged actions. CVE-2025-7519 is a stack-style out-of-bounds write triggered when a policy XML contains 32 or more nested elements. The bug lives in polkit ≤ 0.126 and is being patched across Red Hat Enterprise Linux 6/7/8/9/10, OpenShift 4, SUSE SLE 12/15, Debian, Ubuntu, and Azure Linux 3.
The vendor score of 6.7 MEDIUM is fair on paper but the CVSS vector (AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H) tells the real story: PR:H — the attacker must already be a privileged account able to write into the polkit policy directories. Those directories are root:root 0755 by default. In other words, to exploit this you already need the exact privilege the exploit would grant you. This is a hardening/robustness fix, not a privilege escalation vector in any realistic deployment.
4 steps from start to impact.
Attacker obtains root or equivalent write access to polkit policy directories
/usr/share/polkit-1/actions/ and /etc/polkit-1/rules.d/. Both directories are root-owned and non-world-writable on every supported distro. The attacker must already have root, membership in a wheel-equivalent group with sudo NOPASSWD, or a separate privilege-escalation primitive.- Local shell
- Write access to a root-owned polkit directory
- Ability to drop or edit a .policy XML file
- If the attacker has this, they don't need polkit — they're already root
- SELinux
polkit_etc_rw_tlabels further constrain unrelated writers - Immutable-root and dm-verity images (Fedora Silverblue, CoreOS, Bottlerocket) block the write entirely
PATH rules on /etc/polkit-1/rules.d/ and /usr/share/polkit-1/actions/; FIM tools (Wazuh, Tripwire, AIDE) flag the file drop triviallyCraft a policy XML with ≥32 nested elements
.policy file whose XML tree exceeds the parser's fixed depth handling. A trivial Python one-liner or printf loop generates the payload — no dedicated exploit tool exists on GitHub as of the disclosure window.- Text editor or scripting access on target
- No public weaponized PoC in Metasploit, ExploitDB, or GitHub trending as of July 2026
Trigger polkitd to re-parse the file
polkit.service or waits for the next parse cycle to trigger the OOB write.- Ability to signal or restart polkitd, or wait for inotify
- Requires root already — restart of a system daemon is a privileged op
journalctl -u polkitAchieve crash or (theoretically) code execution as polkitd
- Successful OOB write reaches an exploitable memory region
- Modern glibc heap hardening, FORTIFY_SOURCE, stack canaries, PIE/ASLR
- Zero delta in effective privilege — no vertical movement possible
abrt, systemd-coredump, and any EDR watching SIGSEGV on init-adjacent daemonsstat /etc/polkit-1/rules.d/ and /usr/share/polkit-1/actions/ — both should be root:root 0755. Any deviation is the real finding, not the CVE itself. No mitigation SLA for LOW; verify during your next hardening pass.-w /etc/polkit-1/rules.d/ -p wa -k polkit_policy will flag any policy file drop. This catches both this CVE and the far scarier scenario of an attacker installing a *legitimate* malicious policy. Roll into normal detection engineering backlog.dnf update polkit / apt upgrade policykit-1 / zypper up polkit. LOW verdict = no mitigation SLA; slot into the 365-day remediation window, prioritized behind any HIGH/CRITICAL work..policy files are the only realistic delivery vector for a malformed XML. Add a schema/depth lint to CI to reject nested-element counts >16 before the file ever lands on production.- Network segmentation / firewalls — the vector is local (AV:L). Perimeter controls are irrelevant.
- Disabling polkit — polkit is on the critical path for pkexec, systemd unit auth, NetworkManager, and desktop privilege prompts. Turning it off breaks admin workflows without meaningfully reducing attack surface.
- EDR behavioral rules on pkexec — polkit's CLI helper is not involved in this bug; watching pkexec catches Pwnkit-class issues, not policy-file parser bugs.
The supporting signals.
| In-the-wild exploitation | None observed. Not tracked by CISA KEV, GreyNoise, or Mandiant threat intel as of July 2026 |
|---|---|
| Public PoC | No weaponized PoC. Only the Red Hat Bugzilla reproducer (a nested-element XML fuzz case). No Metasploit or ExploitDB module |
| EPSS | 0.00184 (~0.18%) — bottom-percentile likelihood of exploitation in the next 30 days |
| KEV status | Not listed. No CISA action required |
| CVSS vector | AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H — local, low complexity, high privileges required, unchanged scope |
| Affected versions | polkit ≤ 0.126 on RHEL 6/7/8/9/10, OpenShift 4, SLE 12/15, openSUSE Leap 15.x, Debian, Ubuntu, Azure Linux 3, Amazon Linux 2/2023 |
| Fixed versions | See vendor backports: RHSA on polkit-0.115-x.el7_9, polkit-0.117-x.el8, polkit-0.117-x.el9; SUSE polkit-0.116-x; Debian policykit-1 0.105-33+deb12u1; upstream fix in polkit 0.127 |
| Scanner exposure data | N/A — local-only vector, no network fingerprint. Shodan/Censys/GreyNoise irrelevant |
| Disclosure date | 2025-07-14 via Red Hat Bugzilla #2379675 |
| Reporter | Red Hat Product Security internal fuzzing effort |
Sources.
Why this verdict
- Precondition swallows the payoff: exploiting the bug requires write access to root-owned
/etc/polkit-1/rules.d/or/usr/share/polkit-1/actions/. If an attacker has that, they are already root — the CVE grants zero additional privilege. - No exploitation signal: EPSS 0.00184, not on KEV, no public exploit code, no threat-actor reporting. The bug is a Red Hat internal fuzzing find, not an ITW discovery.
- Role multiplier: polkit runs on nearly every Linux host, but the affected role is *local authorization broker* — not identity, not hypervisor, not CA, not backup, not kernel-mode agent. Blast radius on any host is capped at 'polkitd crash' → local DoS of privilege prompts. Nothing in the noisgate high-value-role catalog applies; the floor stays at MEDIUM baseline and friction pulls it to LOW.
- Realistic worst-case is a nuisance DoS: losing polkitd breaks pkexec and desktop auth prompts — recoverable with a service restart, not a fleet event.
Why not higher?
PR:H is not marketing — the affected file paths are root-owned on every supported distro, and no known primitive turns an unprivileged user into a polkit-policy writer. Without that, the OOB write is unreachable. Nothing in the intelligence set (EPSS, KEV, PoC, ITW) contradicts this.
Why not lower?
The upstream maintainers accepted this as a security fix and every major distro shipped backports, so it is not pure hygiene — a bad admin script or a compromised config-management pipeline (Ansible/Salt/Puppet running as root) could plausibly deliver a malformed policy file and crash polkitd across a fleet. That justifies LOW rather than IGNORE.
Crowdsourced verification payload.
Run on each Linux host as any user (root not required for version check). Invoke: bash check-cve-2025-7519.sh. Compares installed polkit version against the distro's fixed baseline and inspects policy-dir permissions.
#!/usr/bin/env bash
# noisgate: CVE-2025-7519 polkit OOB write check
# Exit 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
VERDICT="UNKNOWN"
REASON=""
if ! command -v pkaction >/dev/null 2>&1 && ! command -v pkexec >/dev/null 2>&1; then
echo "UNKNOWN: polkit not installed"
exit 2
fi
# Detect package manager and installed version
if command -v rpm >/dev/null 2>&1; then
PKG_VER=$(rpm -q --qf '%{VERSION}-%{RELEASE}\n' polkit 2>/dev/null || rpm -q --qf '%{VERSION}-%{RELEASE}\n' polkit-libs 2>/dev/null)
DISTRO="rpm"
elif command -v dpkg >/dev/null 2>&1; then
PKG_VER=$(dpkg-query -W -f='${Version}\n' policykit-1 2>/dev/null || dpkg-query -W -f='${Version}\n' polkitd 2>/dev/null)
DISTRO="deb"
else
echo "UNKNOWN: no supported package manager"
exit 2
fi
if [ -z "${PKG_VER:-}" ]; then
echo "UNKNOWN: could not resolve polkit package version"
exit 2
fi
echo "Installed polkit: $PKG_VER ($DISTRO)"
# Upstream fixed in 0.127. Vendor backports differ; consult errata.
UPSTREAM_VER=$(echo "$PKG_VER" | grep -oE '^[0-9]+\.[0-9]+' | head -1)
if [ -n "$UPSTREAM_VER" ]; then
MAJ=${UPSTREAM_VER%.*}; MIN=${UPSTREAM_VER#*.}
if [ "$MAJ" -eq 0 ] && [ "$MIN" -lt 127 ]; then
# Check for vendor backport marker in changelog
if [ "$DISTRO" = "rpm" ] && rpm -q --changelog polkit 2>/dev/null | grep -q "CVE-2025-7519"; then
VERDICT="PATCHED"
REASON="vendor backport present in RPM changelog"
elif [ "$DISTRO" = "deb" ] && zcat -f /usr/share/doc/policykit-1/changelog.Debian.gz 2>/dev/null | grep -q "CVE-2025-7519"; then
VERDICT="PATCHED"
REASON="vendor backport present in Debian changelog"
else
VERDICT="VULNERABLE"
REASON="polkit ${UPSTREAM_VER} < 0.127 and no CVE-2025-7519 backport marker found"
fi
else
VERDICT="PATCHED"
REASON="polkit ${UPSTREAM_VER} >= 0.127"
fi
fi
# Sanity: policy directory permissions
for d in /etc/polkit-1/rules.d /usr/share/polkit-1/actions; do
if [ -d "$d" ]; then
PERM=$(stat -c '%U:%G %a' "$d")
echo "Policy dir $d -> $PERM"
fi
done
echo "$VERDICT: $REASON"
case "$VERDICT" in
PATCHED) exit 0 ;;
VULNERABLE) exit 1 ;;
*) exit 2 ;;
esac