
CVE-2026-49268 — एक LDAP इंजेक्शन प्रमाणीकरण बायपास भेद्यता का विश्लेषण और उपचार
| Branch | 1.13-CVE-2026-49268 |
| Author | Jinwoo Hwang (https://JinwooHwang.com) |
यह branch (1.13-CVE-2026-49268) Apache Shiro 1.13 release में एक LDAP Injection Authentication Bypass Vulnerability का व्यापक सुरक्षा उपचार समाहित करता है।
आधिकारिक NVD विवरण
एक remote attacker DefaultLdapRealm class में Distinguished Name (DN) निर्माण में LDAP विशेष वर्णों को inject कर सकता है। उपयोगकर्ता द्वारा प्रदान किया गया username input बिना किसी RFC 2253 विशेष वर्ण escaping के सीधे LDAP DN template में concatenate किया जाता है। यह attacker को LDAP bind authentication के लिए उपयोग की जाने वाली DN संरचना में हेरफेर करने की अनुमति देता है, जिससे संभवतः authentication bypass हो सकता है या अन्य उपयोगकर्ताओं का impersonation किया जा सकता है। यह समस्या DefaultLdapRealm का उपयोग करते समय सभी Apache Shiro संस्करणों को 2.2.0 तक, और 3.0.0-alpha-1 को प्रभावित करती है।
| Field | Value |
|---|---|
| CVE | CVE-2026-49268 — Apache Shiro: DefaultLdapRealm में LDAP DN Injection |
| CWE ID | CWE-90 — LDAP Query में उपयोग किए जाने वाले विशेष तत्वों का अनुचित निष्क्रियीकरण ('LDAP Injection') |
| CVSS v4.0 | 8.8 HIGH — 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 CRITICAL — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N |
| प्रभावित संस्करण | org.apache.shiro:shiro-core 0 से 2.2.0 सम्मिलित; 3.0.0-alpha-0 से 3.0.0-alpha-1 सम्मिलित |
| Upstream में ठीक किया गया | 2.2.1 और 3.0.0-alpha-2 |
| संदर्भ | https://lists.apache.org/thread/svszql3od8td7hn6conyj2oq70v53b5s |
Apache Shiro उपयोगकर्ताओं को LDAP directory के विरुद्ध authenticate कर सकता है। ऐसा करने के लिए, यह जमा किए गए username को एक Distinguished Name में बदल देता है — उस उपयोगकर्ता की entry के लिए directory का पता — username को एक configured template में डालकर, उदाहरण के लिए
uid={0},ou=users,dc=mycompany,dc=com।
प्रभावित संस्करणों में username को उस template में raw text के रूप में paste किया जाता है। कुछ विराम चिह्न वर्ण — विशेष रूप से comma — directory server के लिए सामान्य text नहीं हैं; वे वह syntax हैं जो एक address के एक भाग को अगले से अलग करते हैं। इसलिए इन वर्णों वाला username address के भीतर एक value की तरह व्यवहार करना बंद कर देता है और स्वयं address का हिस्सा बनने लगता है।
व्यावहारिक परिणाम यह है कि login करने वाला व्यक्ति यह प्रभावित कर सकता है कि Shiro किस directory entry के विरुद्ध authenticate करने का प्रयास करता है, बजाय केवल अपना नाम प्रदान करने के। deployment जिस container में lookup चाहता था, उसमें खोजे जाने के बजाय lookup को directory में कहीं और redirect किया जा सकता है। directory कैसे व्यवस्थित है और क्या अनुमति देता है, इसके आधार पर यह गलत identity के रूप में authenticate होने, या authentication के सफल होने की ओर ले जा सकता है जबकि ऐसा नहीं होना चाहिए।
जोखिम प्रोफ़ाइल। जोखिम बढ़ाने वाले कारक: किसी पूर्व access या credentials की आवश्यकता नहीं है — input login boundary पर आता है, जो किसी भी व्यक्ति के लिए पहुँच योग्य है जो application तक पहुँच सकता है; और प्रभावित class Shiro को LDAP से जोड़ने का मानक, प्रलेखित तरीका है, इसलिए यह कोई असामान्य configuration नहीं है। जोखिम घटाने वाले कारक: deployment को वास्तव में DefaultLdapRealm (या JndiLdapRealm) एक configured DN template के साथ उपयोग करना चाहिए; जो deployments अन्य साधनों से authenticate करते हैं, या जो पूर्ण DN या प्रमाणपत्र जैसे non-text credential पास करते हैं, वे प्रभावित नहीं हैं। redirect किया गया lookup उपयोगी authentication देता है या नहीं, यह target directory के अपने layout और access rules पर भी निर्भर करता है, जो site के अनुसार भिन्न होते हैं।
एक दूसरा, गैर-सुरक्षा परिणाम release planning के लिए ध्यान देने योग्य है: जिन उपयोगकर्ताओं के वैध usernames में backslash है या जो # से शुरू होते हैं, वे वर्तमान में बिल्कुल भी login नहीं कर सकते, क्योंकि उनके लिए बनाया गया address एक well-formed name नहीं है और सीधे अस्वीकार कर दिया जाता है। अन्य विराम चिह्न सीधे विफल नहीं होते — वे चुपचाप बदल देते हैं कि address किस entry को संदर्भित करता है, जो ऊपर वर्णित सुरक्षा समस्या है। वही fix दोनों को हल करता है।
core/src/main/java/org/apache/shiro/realm/ldap/DefaultLdapRealm.java, getUserDn(String),
unfixed baseline पर पंक्तियाँ 227–250:```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();
}
टेम्पलेट को कॉन्फ़िगरेशन समय पर एक बार, `{0}` टोकन के आसपास (`setUserDnTemplate`, पंक्तियाँ 181–200), एक `prefix` और एक `suffix` में विभाजित किया जाता है। प्रमाणीकरण के समय principal को उनके बीच जोड़ा जाता है। **किसी भी बिंदु पर कोई एन्कोडिंग लागू नहीं की जाती है।** `org/apache/shiro/realm/ldap/` में कहीं भी कोई escaping helper नहीं है।
### 3.2 वह invariant जिसे मान लिया गया था पर कभी लागू नहीं किया गया
आसपास का कोड `getUserDn` के परिणाम को एक सुगठित Distinguished Name मानता है —
इसे सीधे `LdapContextFactory.getLdapContext(...)` को सौंपा जाता है और वहाँ से JNDI को
bind DN के रूप में। यह तभी उचित है जब प्रतिस्थापित principal एक *एकल attribute value* हो।
String concatenation इसे लागू नहीं कर सकता। RFC 2253 (और RFC 4514) DN के भीतर संरचनात्मक
सिंटैक्स के रूप में `,` `+` `"` `\` `<` `>` `;` `=`, एक अग्रणी `#`, और अग्रणी/पिछला whitespace
आरक्षित करते हैं। जब इनमें से कोई भी principal में प्रकट होता है तो उन्हें parser द्वारा
संरचना के रूप में पढ़ा जाता है, सामग्री के रूप में नहीं। invariant — *"principal ठीक एक RDN value
में स्थित होता है"* — को हर downstream consumer द्वारा मान लिया गया था और किसी के द्वारा लागू नहीं किया गया था।
### 3.3 निष्पादन प्रवाह, प्रवेश बिंदु से दोष तक```
submitted credentials (username, password)
│
▼
DefaultLdapRealm.doGetAuthenticationInfo(AuthenticationToken) [line 292]
│
▼
DefaultLdapRealm.getLdapPrincipal(AuthenticationToken) [line 338]
│ principal instanceof String ?
│ ├── no ──► return principal unchanged ── NOT AFFECTED (e.g. X.509)
│ └── yes ──┐
▼ │
DefaultLdapRealm.getUserDn(String) [line 227]
│ prefix == null && suffix == null ?
│ ├── yes ──► return principal unchanged ── NOT AFFECTED ("principal IS the DN")
│ └── no ──┐
▼ │
prefix + principal + suffix ◄── DEFECT: unencoded concatenation [line 246]
│
▼
LdapContextFactory.getLdapContext(userDn, credentials)
│
▼
JNDI bind against the directory using the constructed DN