Project: WASC Threat Classification
Threat Type: Attack
Reference ID: WASC-29
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.
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.
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.
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.
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.
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"
[2] http://www.blackhat.com/presentations/bh-europe-08/Alonso-Parada/Whitepaper/bh-eu-08-alonso-parada-WP.pdf
"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"
[5] http://ldapman.org/
Failure to Sanitize Data into LDAP Queries ('LDAP Injection')
[6] http://cwe.mitre.org/data/definitions/90.html
Comments (0)
You don't have permission to comment on this page.