Details
Affected Software:Apache Shiro Project
Fixed in Version:Revision 887987
Issue Type:LDAP Injection
Original Code: Found Here
Description
This patch fixes a LDAP injection vulnerability in the Apache Shiro Project. A quick glance of the vulnerable code shows several references to LDAP and LDAP queries scattered throughout the sample code. In this example,we see that the Shiro developers originally used the username to dynamically build a LDAP query. The symptoms are very similar to a typical SQL injection. The username is used in a string building technique to build the LDAP query which eventually gets passed to an LDAP server. If an attacker crafts the proper username,they could have the ability to modify the original LDAP query and execute an arbitrary LDAP query of their choosing.
Just as the symptoms are very similar to SQL injection,the fix also looks very similar to code that would be used to fix a SQL injection vulnerability. The developers helped the application make a distinction between code and data by removing the string built LDAP query
Developers Solution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | <div id="_mcePaste"> protected AuthorizationInfo buildAuthorizationInfo(Set<String>roleNames) { return new SimpleAuthorizationInfo(roleNames); } private Set<String>getRoleNamesForUser(String username,LdapContext ldapContext) throws NamingException { Set<String>roleNames; roleNames = new LinkedHashSet<String>(); SearchControls searchCtls = new SearchControls(); searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); String userPrincipalName = username; if (principalSuffix != null) { userPrincipalName += principalSuffix; } - String searchFilter = "(&(objectClass=*)(userPrincipalName="+ userPrincipalName + "))"; + //SHIRO-115 - prevent potential code injection: + String searchFilter = "(&(objectClass=*)(userPrincipalName={0}))"; + Object[] searchArguments = new Object[]{userPrincipalName}; - NamingEnumeration answer = ldapContext.search(searchBase,searchFilter,searchCtls); + NamingEnumeration answer = ldapContext.search(searchBase,searchFilter,searchArguments,searchCtls); while (answer.hasMoreElements()) { SearchResult sr = (SearchResult) answer.next(); if (log.isDebugEnabled()) { log.debug("Retrieving group names for user ["+ sr.getName() + "]"); } Attributes attrs = sr.getAttributes(); if (attrs != null) { NamingEnumeration ae = attrs.getAll(); while (ae.hasMore()) { Attribute attr = (Attribute) ae.next(); if (attr.getID().equals("memberOf")) { Collection<String>groupNames = LdapUtils.getAllAttributeValues(attr); if (log.isDebugEnabled()) { log.debug("Groups found for user ["+ username + "]:"+ groupNames); } Collection<String>rolesForGroups = getRoleNamesForGroups(groupNames); roleNames.addAll(rolesForGroups); } } } } return roleNames; } protected Collection<String>getRoleNamesForGroups(Collection<String>groupNames) { Set<String>roleNames = new HashSet<String>(groupNames.size()); if (groupRolesMap != null) { for (String groupName:groupNames) { String strRoleNames = groupRolesMap.get(groupName); if (strRoleNames != null) { for (String roleName:strRoleNames.split(ROLE_NAMES_DELIMETER)) { if (log.isDebugEnabled()) { log.debug("User is member of group ["+ groupName + "] so adding role ["+ roleName + "]"); } roleNames.add(roleName); } } } } return roleNames; } } </div> |


