
CVE-2026-49268 — Analyse et remédiation d'une vulnérabilité de contournement d'authentification par injection LDAP
| Branche | 1.13-CVE-2026-49268 |
| Auteur | Jinwoo Hwang (https://JinwooHwang.com) |
Cette branche (1.13-CVE-2026-49268) contient une remédiation de sécurité complète d'une vulnérabilité de contournement d'authentification par injection LDAP dans la version 1.13 d'Apache Shiro.
Description officielle NVD
Un attaquant distant peut injecter des caractères spéciaux LDAP dans la construction du Distinguished Name (DN) dans la classe DefaultLdapRealm. L'entrée du nom d'utilisateur fournie par l'utilisateur est directement concaténée dans le modèle de DN LDAP sans aucun échappement des caractères spéciaux RFC 2253. Cela permet à un attaquant de manipuler la structure du DN utilisée pour l'authentification par bind LDAP, contournant potentiellement l'authentification ou usurpant l'identité d'autres utilisateurs. Ce problème affecte toutes les versions d'Apache Shiro jusqu'à la 2.2.0, et la 3.0.0-alpha-1 lors de l'utilisation de DefaultLdapRealm.
| Champ | Valeur |
|---|---|
| CVE | CVE-2026-49268 — Apache Shiro : injection de DN LDAP dans DefaultLdapRealm |
| CWE ID | CWE-90 — Neutralisation incorrecte des éléments spéciaux utilisés dans une requête LDAP (« injection LDAP ») |
| CVSS v4.0 | 8.8 ÉLEVÉ — CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N/S:P/AU:Y/R:A/RE:L/U:Red |
| CVSS v3.1 | 9.1 CRITIQUE — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N |
| Versions affectées | org.apache.shiro:shiro-core 0 à 2.2.0 inclus ; 3.0.0-alpha-0 à 3.0.0-alpha-1 inclus |
| Corrigé en amont dans | 2.2.1 et 3.0.0-alpha-2 |
| Référence | https://lists.apache.org/thread/svszql3od8td7hn6conyj2oq70v53b5s |
Apache Shiro peut authentifier les utilisateurs auprès d'un annuaire LDAP. Pour ce faire, il transforme un
nom d'utilisateur soumis en un Distinguished Name — l'adresse de l'annuaire pour l'entrée de cet utilisateur —
en insérant le nom d'utilisateur dans un modèle configuré, par exemple
uid={0},ou=users,dc=mycompany,dc=com.
Dans les versions affectées, le nom d'utilisateur est inséré dans ce modèle en tant que texte brut. Une poignée de caractères de ponctuation — le plus important étant la virgule — ne sont pas du texte ordinaire pour un serveur d'annuaire ; ils constituent la syntaxe qui sépare une partie d'une adresse de la suivante. Un nom d'utilisateur contenant ces caractères cesse donc de se comporter comme une valeur à l'intérieur de l'adresse et commence à se comporter comme une partie de l'adresse elle-même.
La conséquence pratique est qu'une personne qui se connecte peut influencer l'entrée d'annuaire contre laquelle Shiro tente de s'authentifier, plutôt que de simplement fournir son propre nom. Au lieu d'être recherchée dans le conteneur prévu par le déploiement, la recherche peut être redirigée ailleurs dans l'annuaire. Selon la façon dont l'annuaire est structuré et ce qu'il autorise, cela peut conduire à s'authentifier avec la mauvaise identité, ou à une authentification réussissant alors qu'elle ne le devrait pas.
Profil de risque. Ce qui augmente le risque : aucun accès ni identifiant préalable n'est requis — l'entrée
arrive à la frontière de connexion, qui est accessible à quiconque peut atteindre
l'application ; et la classe affectée est la manière standard et documentée de connecter Shiro à LDAP,
il ne s'agit donc pas d'une configuration exotique. Ce qui le réduit : le déploiement doit réellement utiliser
DefaultLdapRealm (ou JndiLdapRealm) avec un modèle de DN configuré ; les déploiements qui
s'authentifient par d'autres moyens, ou qui transmettent un DN complet ou un identifiant non textuel tel qu'un
certificat, ne sont pas affectés. Qu'une recherche redirigée produise une authentification utilisable
dépend également de la structure et des règles d'accès propres à l'annuaire cible, qui varient selon les sites.
Une seconde conséquence, non liée à la sécurité, mérite d'être notée pour la planification des versions : les utilisateurs dont
les noms d'utilisateur légitimes contiennent une barre oblique inverse ou commencent par # ne peuvent actuellement pas se connecter du tout,
car l'adresse construite pour eux n'est pas un nom bien formé et est rejetée
d'emblée. Les autres ponctuations n'échouent pas d'emblée — elles modifient silencieusement l'entrée à laquelle
l'adresse se réfère, ce qui constitue le problème de sécurité ci-dessus. Le même correctif résout les deux.
core/src/main/java/org/apache/shiro/realm/ldap/DefaultLdapRealm.java, getUserDn(String),
lignes 227–250 sur la base non corrigée :```java
protected String getUserDn(String principal) throws IllegalArgumentException, IllegalStateException {
if (!StringUtils.hasText(principal)) {
throw new IllegalArgumentException("User principal cannot be null or empty for User DN construction.");
}
String prefix = getUserDnPrefix();
String suffix = getUserDnSuffix();
if (prefix == null && suffix == null) {
log.debug("userDnTemplate property has not been configured, indicating the submitted "
+ "AuthenticationToken's principal is the same as the User DN. Returning the method argument "
+ "as is.");
return principal;
}
int prefixLength = prefix != null ? prefix.length() : 0;
int suffixLength = suffix != null ? suffix.length() : 0;
StringBuilder sb = new StringBuilder(prefixLength + principal.length() + suffixLength);
if (prefixLength > 0) {
sb.append(prefix);
}
sb.append(principal); // <-- inserted verbatim
if (suffixLength > 0) {
sb.append(suffix);
}
return sb.toString();
}
Le template est découpé une fois, au moment de la configuration, autour du token `{0}`
(`setUserDnTemplate`, lignes 181–200), en un `prefix` et un `suffix`. Au moment de l'authentification,
le principal est concaténé entre eux. **Aucun encodage n'est appliqué à aucun moment.** Il n'existe
aucun utilitaire d'échappement nulle part dans `org/apache/shiro/realm/ldap/`.
### 3.2 L'invariant supposé mais jamais appliqué
Le code environnant traite le résultat de `getUserDn` comme un Distinguished Name bien formé —
il est transmis directement à `LdapContextFactory.getLdapContext(...)` et de là à JNDI comme
bind DN. Cela n'est valide que si le principal substitué est une *valeur d'attribut unique*.
La concaténation de chaînes ne peut pas garantir cela. La RFC 2253 (et la RFC 4514) réservent
`,` `+` `"` `\` `<` `>` `;` `=`, un `#` en tête, ainsi que les espaces en début et fin comme syntaxe
structurelle au sein d'un DN. Lorsque l'un de ces caractères apparaît dans le principal, il est lu par
l'analyseur comme de la structure, et non comme du contenu. L'invariant — *« le principal occupe exactement une valeur RDN »* —
était supposé par chaque consommateur en aval et appliqué par aucun.