none
.NET LdapConnection Bind functions appear to be corrupting NTLMSSP_AUTH user name field before packet transmission

    Question

  • .NET LdapConnection Bind functions appear to be corrupting credentials before packet transmission.

    Specifically
      LdapConnection.Bind Method ()
      https://msdn.microsoft.com/en-us/library/ts65cab1(v=vs.110).aspx

      LdapConnection.Bind Method (NetworkCredential)
      https://msdn.microsoft.com/en-us/library/ms141825(v=vs.110).aspx

    We use the AD/LDAP connection to import user details into our system.
    We bind to the AD using a single domain account with the username 'importer'

    This works well, for all our customers but one.
    That customer has noticed in their DC event logs a number of failed authentications with the account name 'i' (rather than 'importer').

    I captured packets between the application and the ldap server and made the following observations:
    The NTLMSSP_AUTH Bind request user name field becomes corrupted.
    The other fields look normally encoded.

    The initial bindrequest works, then a subsequent bindrequest will always fail in the same way.

    Wireshark shows that the username in the bindRequest goes from double-byte encoded to 3-byte encoded between API calls.
    e.g. If the user name is 'importer', it looks like this in packets when the bind is successful: i\0m\0p\0o\0r\0t\0e\0r\0

    In later bindRequests "importer" looks like this: i\0\0m\0\0p\0\0o\0\0r\0\0t\0\0e\0\0r\0\0  (i.e. every char is followed by 2 nulls instead of just 1)

    In the server event logs the user name is recorded as "i" because I assume that string functions interpret i\0\0 as a null-terminated 1-character Unicode string.

    I created minimal versions of my application.

    Between the "successful" and "failed" bindrequest nothing is done in the application layer with the ldap connection.

    I have also tried

    1) using hardcoded credentials (i.e. not reading them from a database)
    2) using alternative methods for configuring ldap credentials in the LDAP api.
    3) closing existing connections and then creating NEW connections (complementing with 1&2 above).
    4) Doing the above in a single-threaded and multi=threaded programs.

    None of these resolves the issue.

    Because all my code does is provide credentials to the ldap API and then use other ldap APIs to perform queries, the problem doesn't appear to be in my code.

    It seems to be in .Net runtime, or something else below that, is mutating the credentials before they are sent over the wire.

    Any ideas?
    Thursday, March 1, 2018 6:41 PM

All replies

  • Hi JonKS,

    Thanks for posting here.

    You said that "I created minimal versions of my application", so can you show us some code where you encountered the issue?

    What's more, all your customers attempt to connect to the same LDAP server?

    Best Regards,

    Charles


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Monday, March 5, 2018 5:22 AM
  • Hi

    We have a few thousand customers.

    Each customer has their own LDAP server and completely different environments (they install our product into their networks)

    This is the only customer reporting the issue that I know of.
    It is NOT reproducible in our QA env (where the query returns about 50K objects)
    The customer in question - they expect to have a few hundred objects returned.

    The minimal code looks like this.
    I've removed the boilerplate/logging for brevity.

    private List<SearchResponse> SearchDirectory(LdapConnection connection, string distinguishedName, string searchFilter, System.DirectoryServices.Protocols.SearchScope searchScope, params string[] attributeList)
    {
        List<SearchResponse> result = new List<SearchResponse>();
        SearchResponse response = null;
        int maxResultsToRequest = 100;
        try
        {
            PageResultRequestControl pageRequestControl = new PageResultRequestControl(maxResultsToRequest);
            // used to retrieve the cookie to send for the subsequent request
            PageResultResponseControl pageResponseControl;
            SearchRequest searchRequest = new SearchRequest(distinguishedName, searchFilter, searchScope, attributeList);
            searchRequest.TimeLimit = new TimeSpan(0, 0, 0, 20);
            searchRequest.Controls.Add(pageRequestControl);
    
            while (true) 
            {
                response = (SearchResponse)connection.SendRequest(searchRequest);
                result.Add(response);
                pageResponseControl = (PageResultResponseControl)response.Controls[0];
                if (pageResponseControl.Cookie.Length == 0)
                    break;
                // IF YOU GET HERE THEN, IN THE NEXT LOOP ITERATION, YOU WILL SEE THE PACKET CORRUPTION IN THE USERNAME FIELD
                // IF YOU DON'T LOOP HERE THEN THE CORRUPTION WILL HAPPEN IF YOU CALL SearchDirectory() AGAIN.
                pageRequestControl.Cookie = pageResponseControl.Cookie;
            }
        }
        catch (Exception e)
        {
        }
        return result;
    }
    
    
    void test()
    {
        string query = String.Format("(&(|(objectClass=organizationalUnit)(objectClass=builtinDomain)(objectClass=top)(objectClass=domain)))");
        string[] attributes = { "name", "distinguishedName", "objectClass", "objectguid" };
        LdapConnection connection = new LdapConnection(new LdapDirectoryIdentifier("the customers server address"));
        NetworkCredential nc = new NetworkCredential("importer", "aStrongPassword!");
        connection.Bind(nc);
        List<SearchResponse> res1 = SearchDirectory(connection, "a distinguishedName", query, System.DirectoryServices.Protocols.SearchScope.Base, attributes);
        // IF THE PACKET CORRUPTION DIDN'T HAPPEN YET, IT WILL WHEN YOU CALL THE FUNCTION AGAIN...
        List<SearchResponse> res2 = SearchDirectory(connection, "a distinguishedName", query, System.DirectoryServices.Protocols.SearchScope.Base, attributes);
    }

    I've tried moving the create and disposal of the LdapConnection to inside the SearchDir() function so it is a new connection every time, but this fails in exactly the same way.



    • Edited by JonKS Tuesday, March 6, 2018 3:30 PM
    Monday, March 5, 2018 6:18 PM
  • Hi JonKS,

    Please organize your code by code block below so it's easier for others to review your code.

    Do you get any exception when corruption happens? If so, please provide the exception information.

    In your while loop, can you get the first response rightly?

    Best Regards,

    Charles


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    Tuesday, March 6, 2018 2:20 AM
  • LdapException code=49 Message="The supplied credential is invalid"

    These are the responses packets leading up to the error

    458 8.704885 client server LDAP 404 searchRequest(14) "<ROOT>" baseObject
    460 8.705448 server client LDAP 915 searchResEntry(14) "<ROOT>"  | searchResDone(14) success  [1 result]
    462 8.705954 client server LDAP 134 bindRequest(16) "<ROOT>" , NTLMSSP_NEGOTIATEsasl
    463 8.706248 server client LDAP 314 bindResponse(16) saslBindInProgress , NTLMSSP_CHALLENGE
    This packet contains the corruption
    464 8.706411 client server LDAP 664 bindRequest(17) "<ROOT>" , NTLMSSP_AUTH, User: \isasl
    ...
    ...
    686 11.210021 server client LDAP 164 bindResponse(17) invalidCredentials (8009030C: LdapErr: DSID-0C0904DB, comment: AcceptSecurityContext error, data 52e, v1db1)

    Tuesday, March 6, 2018 5:04 PM
  • Hi JonKS,

    I haven't figured out what's the cause of your customer's issue. I think you can try ways below, but not sure whether we can get useful information.

    1. Try another constructor LdapConnection(LdapDirectoryIdentifier, NetworkCredential), set credential in constructor.

    2. Check LdapConnection.Credential property in your while loop to see if it has been changed before second, third... call.

    3. Use Event Tracing tool to get an event log to see if we can get some hints.

    Hope that you can get and share some results.

    Best Regards,

    Charles


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.




    Wednesday, March 7, 2018 8:05 AM
  • Thanks - I will try these out and provide results when I receive it.
    Thursday, March 8, 2018 3:45 PM
  • Regarding the Event Tracing tool - should that be run on: 

    a) just the ldap server?
    b) just the workstation my code is running on?
    c) both the ldap server and the workstation?

    Thanks

    Thursday, March 8, 2018 5:06 PM
  • Hi JonKS,

    Event Tracing tool should be run on your client, there is an example scenario at the end of this document. I think App1.exe mentioned in that document is your client app.

    Best Regards,

    Charles


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Friday, March 9, 2018 1:29 AM
  • Hi Charles,

    The customer ran the tracing tool with this switch ("-flag 0x1A000F41" on the cmdline)
    After a few mins the capture should have contained at least one credential error (errors were observed in the server's event viewer at the capture time)

    0x00000001 DEBUG_SEARCH
    0x00000040 DEBUG_SPEWSEARCH
    0x00000100 DEBUG_CONNECT
    0x00000200 DEBUG_RECONNECT
    0x00000400 DEBUG_RECEIVEDATA
    0x00000800 DEBUG_BYTES_SENT
    0x02000000 DEBUG_CONNECTION
    0x08000000 DEBUG_API_ERRORS
    0x10000000 DEBUG_ERRORS

    After a few minutes the capture contains at least one session that had a credential error.
    Credential errors were observed in the server's event logs at the time of the capture.

    After running 'tracerpt.exe .\ldap.etl -o -report' against the etl file, the output xml file was about 200MB.

    I've searched both the xml file (and the etl file in a hex editor) and have found error responses from the server for the time frame involved in the error reported in the server event logs.

    I'm seeing these messages in the xml log when there is an error logged on the server:

    "Bailing out of LdapExchangeOpaqueToken because server returned 0x31"

    "LdapGetResponseFromServer: no message found for 0xc."

    "ldap SetConnectionError does not have text for message 0xe."

    I don't see any errors explicitly about credentials.

    I don't see any credential information in the xml or etl files at all, so I'm not sure if I've used the correct logging flag.

    Do you have any advice as to the best flags to use in this case?

    Also, I have no idea if this is relevant or not, but the logs have revealed that there are 2 DCs that the client is connecting to. I see this in the etl/xml file (hast is deliberately obfuscated)

       Successfully connected to host &apos;dc-x.company.com&apos;
    and at other times:
      Successfully connected to host &apos;dc-y.company.com&apos;



    • Edited by JonKS Thursday, April 5, 2018 5:22 PM
    Thursday, April 5, 2018 5:20 PM
  • Hi JonKS,

    I can't figure out what caused this issue since this issue only exists to one of your customer. I also didn't find any errors explicitly about credentials.

    Currently I don't have any idea how to fix it, sorry about that. Hope that other community members can provide some suggestions.

    Best Regards,

    Charles


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, April 18, 2018 8:54 AM