Meaningless - LDAP InjectionMeaningless – LDAP Injection

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>