LDAP injection - C-Sharp

LDAP injection - C-Sharp

Need

Prevention of LDAP injection attacks

Context

  • Usage of C# for building robust and scalable applications
  • Usage of System.DirectoryServices.Protocols for interacting with directory services in .NET applications
  • Usage of System.Net for network communication in .NET development
  • Usage of Microsoft.AspNetCore.Mvc for building web applications with ASP.NET Core MVC

Description

Non compliant code

        public class LdapController : Controller
{
    private readonly LdapConnection _connection;

    public LdapController(LdapConnection connection)
    {
        _connection = connection;
    }

    [HttpPost]
    public IActionResult Authenticate(string username, string password)
    {
        var searchFilter = $"(&(objectClass=user)(sAMAccountName={username}))";

        var searchRequest = new SearchRequest("dc=example,dc=com", searchFilter, SearchScope.Subtree);
        var searchResponse = (SearchResponse)_connection.SendRequest(searchRequest);

        if (searchResponse.Entries.Count > 0)
        {
            var user = searchResponse.Entries[0];
            var networkCredential = new NetworkCredential(user.DistinguishedName, password);
            _connection.Bind(networkCredential);
            return Ok();
        }

        return Unauthorized();
    }
}
        
        

The above code is an example of an LDAP injection vulnerability in a C# backend application using ASP.NET Core.

In this code, the Authenticate method is used to authenticate a user against an LDAP server. The method takes a username and password as parameters, which are directly injected into the LDAP query without any sanitization or validation. The LDAP query is constructed by concatenating the username directly into the search filter string:

                var searchFilter = $"(&(objectClass=user)(sAMAccountName={username}))";

            


This allows an attacker to manipulate the LDAP query by providing a specially crafted username. For instance, an attacker could provide a username like admin)(objectClass=*), which would result in the following search filter:

                "(&(objectClass=user)(sAMAccountName=admin)(objectClass=*))"

            


This would return all objects in the LDAP directory, potentially bypassing authentication or revealing sensitive information.

Furthermore, the password is used directly to bind to the LDAP connection without any validation:

                var networkCredential = new NetworkCredential(user.DistinguishedName, password);
_connection.Bind(networkCredential);

            


This could allow an attacker to provide a blank or weak password to authenticate as any user, depending on the LDAP server configuration.

Steps

  • Use parameterized queries or prepared statements to build LDAP queries.
  • Validate and sanitize user input before using it in LDAP queries.
  • Implement proper input validation to prevent malicious input.
  • Implement strong authentication mechanisms to protect against unauthorized access.
  • Implement access controls to restrict access to sensitive data.
  • Regularly update and patch the LDAP server to address any security vulnerabilities.

Compliant code

        public class LdapController : Controller
{
    private readonly LdapConnection _connection;

    public LdapController(LdapConnection connection)
    {
        _connection = connection;
    }

    [HttpPost]
    public IActionResult Authenticate(string username, string password)
    {
        // Validate and sanitize user input
        if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
        {
            return BadRequest("Username and password must not be empty");
        }

        // Use parameterized queries to prevent LDAP injection
        var searchFilter = new LdapFilterBuilder()
            .Equals("objectClass", "user")
            .And()
            .Equals("sAMAccountName", username)
            .Build();

        var searchRequest = new SearchRequest("dc=example,dc=com", searchFilter, SearchScope.Subtree);
        var searchResponse = (SearchResponse)_connection.SendRequest(searchRequest);

        if (searchResponse.Entries.Count > 0)
        {
            var user = searchResponse.Entries[0];
            var networkCredential = new NetworkCredential(user.DistinguishedName, password);
            _connection.Bind(networkCredential);
            return Ok();
        }

        return Unauthorized();
    }
}
        
        

The original code was vulnerable to LDAP injection because it was building LDAP queries using untrusted data. This could allow an attacker to modify the query, potentially leading to unauthorized access or data exposure.

The fixed code addresses this vulnerability by implementing several security measures:

1. Input Validation: The code checks if the username and password are null or empty. If they are, it returns a bad request. This prevents null or empty values from being used in the LDAP query.

2. Parameterized Queries: The code uses a LdapFilterBuilder to build the LDAP query. This class creates parameterized queries, which means that the username is treated as a literal string, not part of the query. This prevents an attacker from modifying the query.

These changes ensure that the LDAP query is built safely, preventing LDAP injection attacks. Additionally, it's recommended to implement strong authentication mechanisms, access controls, and regularly update and patch the LDAP server to further enhance the security.

References