http://projects.webappsec.org/w/page/13246947/LDAP%20Injection
LDAP Injection
LDAP Injection is an attack technique used to exploit web sites that construct LDAP statements from user-supplied input.
Lightweight Directory Access Protocol (LDAP) is an open-standard protocol for both querying and manipulating X.500 directory services. The LDAP protocol runs over Internet transport protocols, such as TCP. Web applications may use user-supplied input to create custom LDAP statements for dynamic web page requests.
When a web application fails to properly sanitize user-supplied input, it is possible for an attacker to alter the construction of an LDAP statement. When an attacker is able to modify an LDAP statement, the process will run with the same permissions as the component that executed the command. (e.g. Database server, Web application server, Web server, etc.). This can cause serious security problems where the permissions grant the rights to query, modify or remove anything inside the LDAP tree. The same advanced exploitation techniques available in SQL Injection can also be similarly applied in LDAP Injection.
LDAP注入是一种攻击网站的的技术,这类被攻击的网站会利用用户提供的输入构造LDAP语句。
轻量级目录访问协议(LDAP)是一个开放标准协议,该协议用于查询和操作X.500的目录服务。LDAP协议同TCP类似,运行于网络传输层。网页应用程序会利用用户输入创建自定义的LDAP语句以实现动态网页请求。网页应用程序如果不能对用户提供的输入进行适当的审查,会为攻击者创造改变LDAP语句的机会。攻击者修改的LDAP语句可以以一定的权限运行,这种权限同运行该语句的组件(如数据库服务器、网页应用程序服务器、网页服务器等)的权限相同。当这种权限具有查询、修改或删除LDAP树内的任何文件时,会造成严重的安全问题。可用于SQL注入的高级利用技术同样可以用于相似的LDAP注入中。
Example
Vulnerable code:
line 1 using System; line 2 using System.Configuration; line 3 using System.Data; line 4 using System.Web; line 5 using System.Web.Security; line 6 using System.Web.UI; line 7 using System.Web.UI.HtmlControls; line 8 using System.Web.UI.WebControls; line 9 using System.Web.UI.WebControls.WebParts; line 10 line 11 using System.DirectoryServices; line 12 line 13 public partial class _Default : System.Web.UI.Page line 14 { line 15 protected void Page_Load(object sender, EventArgs e) line 16 { line 17 string userName; line 18 DirectoryEntry entry; line 19 line 20 userName = Request.QueryString["user"]; line 21 line 22 if (string.IsNullOrEmpty(userName)) line 23 { line 24 Response.Write("<b>Invalid request. Please specify valid user name</b></br>"); line 25 Response.End(); line 26 line 27 return; line 28 } line 29 line 30 DirectorySearcher searcher = new DirectorySearcher(); line 31 line 32 searcher.Filter = "(&(samAccountName=" + userName + "))"; line 33 line 34 SearchResultCollection results = searcher.FindAll(); line 35 line 36 foreach (SearchResult result in results) line 37 { line 38 entry = result.GetDirectoryEntry(); line 39 line 40 Response.Write("<p>"); line 41 Response.Write("<b><u>User information for : " + entry.Name + "</u></b><br>"); line 42 line 43 foreach (string proName in entry.Properties.PropertyNames) line 44 { line 45 Response.Write("<br>Property : " + proName); line 46 line 47 foreach( object val in entry.Properties[proName] ) line 48 { line 49 Response.Write("<br>Value: " + val.ToString()); line 50 } line 51 } line 52 line 53 Response.Write("</p>"); line 54 } line 55 } line 56 }
Looking at the code, we see on line 20 that the userName variable is initialized with the parameter user and then quickly validated to see if the value is empty or null. If the value is not empty, the userName is used to initialize the filter property on line 32. In this scenario, the attacker has complete control over what will be queried on the LDAP server, and he will get the result of the query when the code hits line 34 to 53 where all the results and their attributes are displayed back to the user.
分析以上代码,我们发现在第20行userName变量用user参数进行初始化,并判断该值是否为空。如果这个值不为空,在第32行,userName会被用于初始化过滤属性。在这种情况下,攻击者完全可以在LDAP服务器上查询任何东西,并且在第34-53行所有的查询结果和属性都回馈给了用户。
Attack Example
攻击示例
http://example/default.aspx?user=*
In the example above, we send the * character in the user parameter which will result in the filter variable in the code to be initialized with(samAccountName=*). The resulting LDAP statement will make the server return any object that contains the samAccountName attribute. In addition, the attacker can specify other attributes to search for and the page will return an object matching the query.
在这个例子中,我们在user参数中发送*字符,该字符会初始化samAccountName(samAccountName=*)。LDAP会使服务器返回所有含有samAccountName属性的对象。另外,攻击者可以指定其他属性去查询,该页面会返回满足查询的对象。
Mitigation
缓解方法
The escape sequence for properly using user supplied input into LDAP differs depending on if the user input is used to create the DN (Distinguished Name) or used as part of the search filter. The listings below shows the character that needs to be escape and the appropriate escape method for each case.
对于用户提供的输入至LDAP中的转义序列理应根据用户输入的用途分别对待,用户输入可以用于创建DN(可分辨的名字)或者作为查询过滤器的一部分。下面列举的字符应该被过滤或者对不同的情况采取不同的过滤策略。
Used in DN - Requires \ escape
- &
- !
- |
- =
- <
- >
- ,
- +
- -
- "
- '
- ;
Used in Filter- Requires {\ASCII} escape
- ( {\28}
- ) {\29}
- \ {\5c}
- * {\2a}
- / {\2f}
- NUL {\0}
The code below implements the escape logic for both DN and Filter case. Use CanonicalizeStringForLdapFilter() to escape when the input is used to create the filter and CanonicalizeStringForLdapDN()for DN. In addition, both IsUserGivenStringPluggableIntoLdapSearchFilter and IsUserGivenStringPluggableIntoLdapDN can be used to detect the presence of restricted characters.
下面的代码实现了DN和过滤情况的逻辑过滤。使用函数 CanonicalizeStringForLdapFilter()去过滤用户输入作为创建过滤器的情况,而用CanonicalizeStringForLdapDN()函数去过滤创建用户的情况。另外,IsUserGivenStringPluggableIntoLdapSearchFilter 和 IsUserGivenStringPluggableIntoLdapDN两个函数可以检测限制符号的存在。
line 1 using System; line 2 using System.Collections.Generic; line 3 using System.Text; line 4 line 5 namespace LdapValidation line 6 { line 7 public class LdapCanonicaliztion line 8 { line 9 /// <summary> line 10 /// Characters that must be escaped in an LDAP filter path line 11 /// WARNING: Always keep '\\' at the very beginning to avoid recursive replacements line 12 /// </summary> line 13 private static char[] ldapFilterEscapeSequence = new char[] { '\\', '*', '(', ')', '\0', '/' }; line 14 line 15 /// <summary> line 16 /// Mapping strings of the LDAP filter escape sequence characters line 17 /// </summary> line 18 private static string[] ldapFilterEscapeSequenceCharacter = new string[] { "\\5c", "\\2a", "\\28", "\\29", "\\00", "\\2f" }; line 19 line 20 /// <summary> line 21 /// Characters that must be escaped in an LDAP DN path line 22 /// </summary> line 23 private static char[] ldapDnEscapeSequence = new char[] { '\\', ',', '+', '"', '<', '>',';' }; line 24 line 25 /// <summary> line 26 /// Canonicalize a ldap filter string by inserting LDAP escape sequences. line 27 /// </summary> line 28 /// <param name="userInput">User input string to canonicalize</param> line 29 /// <returns>Canonicalized user input so it can be used in LDAP filter</returns> line 30 public static string CanonicalizeStringForLdapFilter(string userInput) line 31 { line 32 if (String.IsNullOrEmpty(userInput)) line 33 { line 34 return userInput; line 35 } line 36 line 37 string name = (string)userInput.Clone(); line 38 line 39 for (int charIndex = 0; charIndex < ldapFilterEscapeSequence.Length; ++charIndex) line 40 { line 41 int index = name.IndexOf(ldapFilterEscapeSequence[charIndex]); line 42 if (index != -1) line 43 { line 44 name = name.Replace(new String(ldapFilterEscapeSequence[charIndex], 1), ldapFilterEscapeSequenceCharacter[charIndex]); line 45 } line 46 } line 47 line 48 return name; line 49 } line 50 line 51 /// <summary> line 52 /// Canonicalize a ldap dn string by inserting LDAP escape sequences. line 53 /// </summary> line 54 /// <param name="userInput">User input string to canonicalize</param> line 55 /// <returns>Canonicalized user input so it can be used in LDAP filter</returns> line 56 public static string CanonicalizeStringForLdapDN(string userInput) line 57 { line 58 if (String.IsNullOrEmpty(userInput)) line 59 { line 60 return userInput; line 61 } line 62 line 63 string name = (string)userInput.Clone(); line 64 line 65 for (int charIndex = 0; charIndex < ldapDnEscapeSequence.Length; ++charIndex) line 66 { line 67 int index = name.IndexOf(ldapDnEscapeSequence[charIndex]); line 68 if (index != -1) line 69 { line 70 name = name.Replace(new string(ldapDnEscapeSequence[charIndex], 1), @"\" + ldapDnEscapeSequence[charIndex] ); line 71 } line 72 } line 73 line 74 return name; line 75 } line 76 line 77 /// <summary> line 78 /// Ensure that a user provided string can be plugged into an LDAP search filter line 79 /// such that there is no risk of an LDAP injection attack. line 80 /// </summary> line 81 /// <param name="userInput">String value to check.</param> line 82 /// <returns>True if value is valid or null, false otherwise.</returns> line 83 public static bool IsUserGivenStringPluggableIntoLdapSearchFilter(string userInput) line 84 { line 85 if (string.IsNullOrEmpty(userInput)) line 86 { line 87 return true; line 88 } line 89 line 90 if (userInput.IndexOfAny(ldapDnEscapeSequence) != -1) line 91 { line 92 return false; line 93 } line 94 line 95 return true; line 96 } line 97 line 98 /// <summary> line 99 /// Ensure that a user provided string can be plugged into an LDAP DN line 100 /// such that there is no risk of an LDAP injection attack. line 101 /// </summary> line 102 /// <param name="userInput">String value to check.</param> line 103 /// <returns>True if value is valid or null, false otherwise.</returns> line 104 public static bool IsUserGivenStringPluggableIntoLdapDN(string userInput) line 105 { line 106 if (string.IsNullOrEmpty(userInput)) line 107 { line 108 return true; line 109 } line 110 line 111 if (userInput.IndexOfAny(ldapFilterEscapeSequence) != -1) line 112 { line 113 return false; line 114 } line 115 line 116 return true; line 117 } line 118 } line 119 }
References
"LDAP Injection: Are Your Web Applications Vulnerable?", By Sacha Faust - SPI Dynamics
[1] http://ebook.security-portal.cz/book/hacking_method/LDAP/LDAPinjection.pdf
"LDAP Injection & Blind LDAP Injection"
"A String Representation of LDAP Search Filters"
[3] http://www.ietf.org/rfc/rfc1960.txt
"Understanding LDAP"
[4] http://www.redbooks.ibm.com/redbooks/SG244986.html
"LDAP Resources"
Failure to Sanitize Data into LDAP Queries ('LDAP Injection')