← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2026-0251 · CWE-426 · Disclosed 2026-05-13

Multiple local privilege escalation vulnerabilities in the Palo Alto Networks GlobalProtect™ app

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

This is a valet key that becomes dangerous only after the attacker is already inside the car

Palo Alto says CVE-2026-0251 covers multiple local privilege escalation bugs in the GlobalProtect app that let a non-admin user run arbitrary commands as NT AUTHORITY\SYSTEM on Windows and as root on macOS and Linux. Affected ranges are Windows/macOS 6.0.0 through 6.0.12, Windows/macOS 6.2.0 through 6.2.8-h9, Windows/macOS 6.3.0 through 6.3.3-h10, Linux 6.0.0 through 6.0.10, Linux 6.2.0 through 6.2.9, and Linux 6.3.0 through 6.3.3-h1; Android, ChromeOS, iOS, and UWP are not affected.

The important part is the friction audit: this is not initial access, not remote, and not internet-scalable. Palo Alto only discloses CWE-426, so the most plausible abuse pattern is a local search-path hijack against a privileged GlobalProtect component; that is serious on an already-compromised workstation, but it does not behave like a perimeter breaker. That keeps it in MEDIUM instead of HIGH despite the ugly end state.

"ASSESSED AT MEDIUM: full SYSTEM/root impact, but only after an attacker already has local user-level foothold"
02 · The Attack Path

4 steps from start to impact.

STEP 01

Land as a standard user with a commodity loader

The attacker first needs code execution or an interactive session as a normal user on a machine running a vulnerable GlobalProtect client. In practice that means phishing malware, a browser exploit, a malicious insider, or an existing foothold from another endpoint control failure. This CVE does not provide that initial access.
Conditions required:
  • Local user account or user-level code execution on the endpoint
  • GlobalProtect app installed in an affected version range
Where this breaks in practice:
  • Email security, browser isolation, EDR, and application control should block many pre-CVE footholds
  • This prerequisite implies the attacker is already past your primary prevention layers
Detection/coverage: Vuln scanners can inventory versions, but step 1 is usually caught by EDR, identity telemetry, and email/web controls rather than by CVE-specific signatures.
STEP 02

Stage a search-path hijack payload

Based on Palo Alto's CWE-426 classification, the attacker likely abuses an untrusted search path by planting a malicious executable or library where a privileged GlobalProtect component will search for it. The weaponized tool here is a custom search-path hijack payload using the ATT&CK T1574 family rather than an internet exploit kit.
Conditions required:
  • A writable path that is searched by the vulnerable GlobalProtect component
  • Knowledge of the target filename or load order
Where this breaks in practice:
  • Exact path/name requirements break many opportunistic attempts
  • Least-privilege filesystem ACLs, tamper protection, and app control can block the file drop
Detection/coverage: Look for suspicious file creation near GlobalProtect install or helper paths and unsigned binaries appearing shortly before service/helper execution.
STEP 03

Trigger the privileged helper or service

The attacker then causes the vulnerable component to start, restart, reconnect, or otherwise resolve the poisoned path. The tool at this step is usually a LOLBIN trigger such as sc.exe, launchctl, systemctl, or simply a reconnect/login event that wakes the helper.
Conditions required:
  • Ability to wait for or trigger the affected GlobalProtect process path resolution
  • The service/helper runs with elevated privileges
Where this breaks in practice:
  • Service restart rights may be limited for non-admins
  • Some environments may require timing the attack around reconnects, logons, or client updates
Detection/coverage: EDR should flag unusual parent-child chains from PanGPS, PanGPA, launch daemons, or Linux service units into unexpected child binaries.
STEP 04

Convert user foothold into SYSTEM/root execution

If the hijack works, the payload runs with administrative privileges and the attacker can disable defenses, dump secrets, persist deeper, or pivot with far fewer restrictions. The weaponized tool here is whatever post-escalation tradecraft the operator already uses: cmd.exe/powershell.exe on Windows or shell tooling on macOS/Linux.
Conditions required:
  • Successful privileged execution via the vulnerable component
Where this breaks in practice:
  • Post-escalation actions are noisy and often high-signal to EDR
  • Modern endpoint controls can still contain blast radius after privilege escalation
Detection/coverage: This is the highest-confidence stage for defenders: SYSTEM/root child processes, credential access attempts, and security control tampering are all mature EDR detections.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo active exploitation signal in the sources reviewed: Palo Alto says it was *not aware of malicious exploitation* as of the advisory update, and the CVE is *not* in CISA KEV.
Proof-of-concept availabilityNo public PoC was surfaced in the official/advisory-linked material; Palo Alto marks exploit maturity as UNREPORTED. Given the CWE-426 classification, a private search-path hijack proof is plausible, but there is no public weaponization evidence in the reviewed sources.
EPSSUser-provided EPSS is 0.00007; Tenable shows 0.00006 on its retrieved page, which is consistent with an extremely low near-term exploitation probability. The fetched public pages did not surface a reliable percentile for citation.
KEV statusNot listed in the Known Exploited Vulnerabilities Catalog. That matters because this is the strongest public exploitation triage input most enterprise patch teams use.
Vector reality checkCurrent vendor/NVD-enriched scoring shows CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N with a 5.9 Medium label. Translation: easy to use once local code exists, but local-only and low-privilege dependent.
Affected versionsWindows/macOS: < 6.0.13, < 6.2.8-h10, < 6.3.3-h11. Linux: < 6.0.11, all 6.2.0 through 6.2.9, and < 6.3.3-h2. Android, ChromeOS, iOS, and UWP are unaffected per the vendor advisory.
Fixed versionsWindows/macOS fixed streams are 6.0.13, 6.2.8-h10 (6.2.8-948), and 6.3.3-h11 (6.3.3-c1016); Linux fixed streams are 6.0.11 and 6.3.3-h2 (6.3.3-c42), with Linux 6.2.x customers effectively moving to the 6.3.3-h2 train.
Exposure populationThis is a client-side endpoint issue, so internet census tools are the wrong lens: Censys Search maps internet-exposed services, but that does not tell you how many laptops have a vulnerable GlobalProtect app installed. Your CMDB, EDR, MDM, and software inventory are the authoritative exposure sources here.
Disclosure timelinePublished on 2026-05-13; advisory updated through 2026-06-02, including version corrections for 6.3.x Windows/macOS and acknowledgement updates. That history matters because early downstream mirrors showed stale fixed-version guidance.
Reporter / discovery sourceThe vendor says the issue was discovered externally and credits *internal security research teams, Liang Zhu, and Martijn van Ramesdonk* in the acknowledgement section of the advisory.
04 · The Call

noisgate verdict.

Final Verdict
= UNCHANGED to MEDIUM (6.1/10)

The single biggest severity suppressor is that exploitation requires an attacker to already have a local low-privilege foothold on the endpoint. That makes this a post-initial-access privilege escalator, not an internet-scale entry point, even though successful abuse ends in full SYSTEM/root control.

HIGH Attack preconditions are materially limiting: local access plus user-level execution is required
MEDIUM Exact exploit mechanics are inferred from `CWE-426` because the vendor did not publish step-by-step technical details

Why this verdict

  • Starts dangerous on impact: successful exploitation gives arbitrary command execution as SYSTEM or root, which is endpoint-total compromise.
  • Downward pressure: attacker position is already post-breach. The chain requires local user context or user-level malware, which implies the attacker has already beaten your initial-access controls.
  • Downward pressure: reachable population is narrower than it looks. GlobalProtect is widely deployed, but only installed endpoints in specific version trains are relevant, and the bug is not remotely enumerable from the public internet.
  • Slight upward pressure: blast radius per host is high. On a managed VPN client, SYSTEM/root lets an operator tamper with defenses and deepen persistence, so this is not backlog junk.

Why not higher?

There is no unauthenticated remote path, no network exposure requirement, no active exploitation evidence, and no public PoC in the reviewed sources. For a 10,000-host environment, this lands in the bucket of important endpoint hardening and privilege containment, not perimeter emergency response.

Why not lower?

Once the attacker reaches the vulnerable step, the payoff is full administrative execution with no user interaction required. A local PE that turns a normal user into SYSTEM/root on a widely deployed enterprise VPN client is still consequential enough that dismissing it as LOW would understate the per-host blast radius.

05 · Compensating Control

What to do — in priority order.

  1. Inventory GlobalProtect versions from EDR/MDM — Use software inventory to identify Windows, macOS, and Linux endpoints in the affected streams and separate them by version train. For a MEDIUM verdict there is no noisgate mitigation SLA, so do this as part of normal vulnerability operations while you work toward the remediation window.
  2. Tighten application control around privileged client paths — Constrain unsigned or user-writable binaries from being executed or loaded by privileged GlobalProtect components, especially via WDAC/AppLocker, macOS execution controls, and Linux package/file integrity controls. This is one of the few meaningful compensating controls for a likely search-path hijack pattern; there is no mitigation SLA for MEDIUM, but keep it in place until patched.
  3. Harden user-writable search paths — Review filesystem ACLs, PATH-like resolution behavior, and writable directories adjacent to GlobalProtect helper/service execution contexts so a standard user cannot plant executable content in privileged search locations. That reduces exploit reliability immediately even where patch rollout is slower.
  4. Hunt for elevated child-process anomalies — Create detections for unexpected child processes of GlobalProtect components such as PanGPS, PanGPA, launch daemons, and Linux service units, especially where the child binary is unsigned or launched from user space. This does not fix the bug, but it gives you the best chance to catch exploitation if an already-compromised endpoint tries to cash in the PE.
What doesn't work
  • Perimeter controls like WAF, VPN portal ACLs, or internet blocking do not help because this is not a network-reachable flaw in the portal/gateway plane.
  • MFA does not stop local privilege escalation after the attacker already has code execution on the endpoint.
  • Only patching PAN-OS GlobalProtect gateways does nothing for this CVE; the vulnerable component is the endpoint app.
06 · Verification

Crowdsourced verification payload.

Run this on the target endpoint itself from a local shell or your EDR live-response console: python3 gp_cve_2026_0251_check.py on macOS/Linux or py gp_cve_2026_0251_check.py on Windows. It needs only normal user rights for most checks, though some locked-down Windows installs may require local admin to read all uninstall registry keys; output is VULNERABLE, PATCHED, or UNKNOWN.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/usr/bin/env python3
# CVE-2026-0251 GlobalProtect version check
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

import os
import platform
import plistlib
import re
import subprocess
import sys
from pathlib import Path

APP_NAME = 'GlobalProtect'

class V:
    def __init__(self, major, minor, patch, hotfix=0):
        self.major = major
        self.minor = minor
        self.patch = patch
        self.hotfix = hotfix
    def key(self):
        return (self.major, self.minor, self.patch, self.hotfix)
    def __lt__(self, other):
        return self.key() < other.key()
    def __le__(self, other):
        return self.key() <= other.key()
    def __ge__(self, other):
        return self.key() >= other.key()
    def __gt__(self, other):
        return self.key() > other.key()
    def __repr__(self):
        return f'{self.major}.{self.minor}.{self.patch}-h{self.hotfix}' if self.hotfix else f'{self.major}.{self.minor}.{self.patch}'


def parse_version(text):
    if not text:
        return None
    # Accept versions like 6.3.3-h11, 6.3.3, 6.2.8-948, 6.3.3-c1016
    m = re.search(r'(\d+)\.(\d+)\.(\d+)(?:-h(\d+))?', text)
    if not m:
        return None
    major, minor, patch = int(m.group(1)), int(m.group(2)), int(m.group(3))
    hotfix = int(m.group(4)) if m.group(4) else 0
    return V(major, minor, patch, hotfix)


def run_cmd(cmd):
    try:
        p = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
        out = (p.stdout or '') + '\n' + (p.stderr or '')
        return out.strip()
    except Exception:
        return ''


def detect_windows_version():
    # Registry via PowerShell (most reliable without pywin32 dependency)
    ps = r'''
$paths = @(
  'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
  'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*',
  'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
$items = foreach ($p in $paths) {
  Get-ItemProperty -Path $p -ErrorAction SilentlyContinue |
    Where-Object { $_.DisplayName -match 'GlobalProtect' } |
    Select-Object -First 1 DisplayName, DisplayVersion
}
$first = $items | Select-Object -First 1
if ($first) { $first.DisplayVersion }
'''
    out = run_cmd(['powershell', '-NoProfile', '-Command', ps])
    v = parse_version(out)
    if v:
        return v, out.strip()

    # Fallback: WMIC may still exist on some systems
    out = run_cmd(['wmic', 'product', 'where', "name like 'GlobalProtect%%'", 'get', 'version'])
    v = parse_version(out)
    if v:
        return v, out.strip()

    return None, ''


def detect_macos_version():
    candidates = [
        '/Applications/GlobalProtect.app/Contents/Info.plist',
        '/Library/PreferencePanes/GlobalProtect.prefPane/Contents/Info.plist'
    ]
    for p in candidates:
        try:
            if Path(p).exists():
                with open(p, 'rb') as f:
                    info = plistlib.load(f)
                text = str(info.get('CFBundleShortVersionString') or info.get('CFBundleVersion') or '')
                v = parse_version(text)
                if v:
                    return v, text
        except Exception:
            pass
    out = run_cmd(['defaults', 'read', '/Applications/GlobalProtect.app/Contents/Info', 'CFBundleShortVersionString'])
    v = parse_version(out)
    if v:
        return v, out.strip()
    return None, ''


def detect_linux_version():
    # App CLI
    for cmd in [
        ['globalprotect', 'version'],
        ['globalprotect', 'show', '--version'],
        ['/opt/paloaltonetworks/globalprotect/globalprotect', 'version']
    ]:
        out = run_cmd(cmd)
        v = parse_version(out)
        if v:
            return v, out.strip()

    # Debian/Ubuntu package query
    out = run_cmd(['dpkg-query', '-W', '-f=${Package} ${Version}\n', 'globalprotect'])
    v = parse_version(out)
    if v:
        return v, out.strip()

    # RPM query
    for pkg in ['GlobalProtect', 'globalprotect']:
        out = run_cmd(['rpm', '-q', pkg])
        v = parse_version(out)
        if v:
            return v, out.strip()

    return None, ''


def in_range(v, start, end_exclusive):
    return v >= start and v < end_exclusive


def assess_windows_or_macos(v):
    # Advisory-covered vulnerable windows/macOS streams
    if in_range(v, V(6, 0, 0), V(6, 0, 13)):
        return 'VULNERABLE'
    if in_range(v, V(6, 2, 0), V(6, 2, 8, 10)):
        return 'VULNERABLE'
    if in_range(v, V(6, 3, 0), V(6, 3, 3, 11)):
        return 'VULNERABLE'

    # Known-not-listed stream: 6.1.x not present in vendor matrix
    if v.major == 6 and v.minor == 1:
        return 'UNKNOWN'

    # Newer branches or fixed branches treated as patched
    return 'PATCHED'


def assess_linux(v):
    if in_range(v, V(6, 0, 0), V(6, 0, 11)):
        return 'VULNERABLE'
    # Vendor says 6.2.0 through 6.2.9 are vulnerable
    if v.major == 6 and v.minor == 2 and v <= V(6, 2, 9, 999):
        return 'VULNERABLE'
    if in_range(v, V(6, 3, 0), V(6, 3, 3, 2)):
        return 'VULNERABLE'

    if v.major == 6 and v.minor == 1:
        return 'UNKNOWN'

    return 'PATCHED'


def main():
    sysname = platform.system().lower()
    version_obj = None
    raw = ''

    if 'windows' in sysname:
        version_obj, raw = detect_windows_version()
        platform_name = 'Windows'
    elif 'darwin' in sysname:
        version_obj, raw = detect_macos_version()
        platform_name = 'macOS'
    elif 'linux' in sysname:
        version_obj, raw = detect_linux_version()
        platform_name = 'Linux'
    else:
        print(f'UNKNOWN - unsupported platform: {platform.system()}')
        sys.exit(2)

    if not version_obj:
        print(f'UNKNOWN - {APP_NAME} version not detected on {platform_name}')
        sys.exit(2)

    if platform_name in ('Windows', 'macOS'):
        verdict = assess_windows_or_macos(version_obj)
    else:
        verdict = assess_linux(version_obj)

    print(f'{verdict} - platform={platform_name} detected_version={version_obj} raw="{raw}"')
    if verdict == 'PATCHED':
        sys.exit(0)
    elif verdict == 'VULNERABLE':
        sys.exit(1)
    else:
        sys.exit(2)


if __name__ == '__main__':
    main()
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning, do not treat this like a perimeter fire drill; treat it like an endpoint privilege-escalation cleanup item. Use EDR/MDM to inventory affected GlobalProtect versions this week, fold fixes into your normal endpoint agent rollout, and remember that for a MEDIUM verdict there is no noisgate mitigation SLA — go straight to the 365-day remediation window; your noisgate remediation SLA is to complete vendor patching within 365 days. If you cannot patch quickly on sensitive admin workstations, apply the compensating controls above now as hardening, but this does not warrant emergency change windows absent your own exploitation telemetry.

Sources

  1. Palo Alto Networks advisory for CVE-2026-0251
  2. NVD entry for CVE-2026-0251
  3. CISA Known Exploited Vulnerabilities Catalog
  4. FIRST EPSS data and scoring notes
  5. Tenable CVE page showing EPSS and downstream risk data
  6. GlobalProtect 6.3.3-h11 Windows and macOS addressed issues
  7. GlobalProtect 6.3.3-h2 Linux addressed issues
  8. Censys Search overview
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.