LDAP and Citrix ADC / NetScaler


last update: November 3rd 2020

This is the first part of debugging logon problems. The second one, an explanation of aaad.debug log, may be found here.

Recently I had to debug LDAP authentication on Citrix ADC / NetScaler and I started digging deeper.

I wanted to know how LDAP authentication really works, so I did what I always do in a case like that: I started with a network trace.

Attention: in 2018, when doing the original blog, plain text LDAP, TCP 389, had been common practice. Things changed. We now use secure LDAP, TCP 636. This will (in long future) be the only way supported by Microsoft.

LDAP on port 636 is fully encrypted, so it would not be possible to explain the flow od date, that’s why this blog refers to plain text LDAP.

Attention! Different to default, my NetScaler is load-balancing LDAP-Servers. Therefore all packets don’t origin from NetScaler IP (NSIP) but from subnet-IP (SNIP). Usually all authentication is done by BSD and therefore origins from NSIP. I recommend load balancing LDAP servers!

I did analysis on all of these packets.

  1. bindRequest(1) “LDAP@intern.norz.at” simple
  2. bindResponse(1) success
  3. searchRequest(2) “dc=…” wholeSubtree
  4. seachResEntry(2) “dc=…” | seachResEntry(2) “CN=…”
  5. bindRequest(3) “CN=…” simple
  6. bindResponse(3) success
    bindResponse(3) failed
  7. unbindRequest(4)

1st Packet (266), bindRequest(1)

network trace for a bind request on Citrix ADC / NetScaler

So this is a bind request for LDAP2@intern.norz.at (called Administrator Bind DB, a miss-leading word as this should not be an admin at all for security reasons). In this case it is a service user. It’s permissions would be read only to the whole Active Directory, a permission usually every domain user has. It does not even need the user right log on locally.

Minimum requirements for Administrator Bind DN would be following:

  • read for the Base DN (and the tree below) and the user object itself.
  • read attributes like UPN or sAMAccountName containing the user name or attributes used in filters
  • read for groups (only if you need group extraction)
  • read for attributes PwdLastSet, UserAccountControl, msDS-User-Account-Control-Computed if you need to know if passwords are expired (and trigger password change from Citrix ADC / NetScaler)

In last line we see the MD5 hashed password (simple: …) belonging to this administrative bind DN user.

2nd packet, response from LDAP server, bindResponse(1)

network trace for a bind request on Citrix ADC / NetScalerThis is a simple success message. No more words needed here.

3rd packet, searching for the user’s LDAP name,

network trace for a bind request on Citrix ADC / NetScaler

As mentioned above, we have to locate the user object to get the LDAP name for this user. The Base DN is the top most location in AD, where this user may be found, and of course we will search the whole AD below this location. In my case I search all of a LDAP-directory called intern.norz.at. To restrict to an OU called Enterprise Users we would set Base DN to: ou=Enterprise Users, dc=intern, dc=norz, dc=at.

4th packet, user’s LDAP name and group membership, seachResEntry(2)

network trace for LDAP a bind request on Citrix ADC / NetScalerFor reasons of readability I collapsed some results of minor interest.

The first section is about active directory properties needed. But we are interested in the second section: That’s where user object is located!

First of all, we located a user with LDAP name CN=Johannes Norz, OU=Users, OU=Our’s, DC=intern, DC=norz, DC=at. This user is located in an OU called users which is located in an other OU called Our’s. Normal users would not be able to remember user names like that, that’s why we have to do this step.

We also see, this user is member of two groups: PublishedApps and FaNorz (we see the LDAP names of these groups). This groups will be available from Citrix ADC / NetScaler. This user is also member of Domain Users, but we don’t see this group due to AD internal reasons. That’s why it’s not available in Citrix ADC / NetScaler.

5th packet: Logging on, bindRequest(3)

network trace for LDAP a bind request on Citrix ADC / NetScalerThis one is an important one. We see, we ask to log on a user called CN=Johannes Norz, OU=Users, OU=Our’s, DC=intern, DC=norz, DC=at and a password “top secret Password”

The password is transferred in plain test!

Maybe you don’t like it, but that’s what it is. So you should never use security type TCP PLAINTEXT (TCP 389) for LDAP authentication! Rather use SSL (TCP 636) or TLS (TCP 398) instead! This will encrypt all communication and therefore protect passwords.

6th packet: Success or fail, bindResponse(3)

network trace for LDAP a bind request on Citrix ADC / NetScalerThis is just a message telling us: We had been lucky guessing this highly sophisticated password.

network trace for failed LDAP a bind request on Citrix ADC / NetScaler
Well, this one is the one we won’t like as it’s a failed logon. But we can clearly see: It failed due to invalid credentials!

7th packet: Closing the door, unbindRequest(4)

We are finished, so we tell LDAP to close the connection.

About the author

Johannes Norz

Johannes Norz is a Citrix Certified Citrix Technology Advocate (CTA), Citrix Certified Instructor (CCI) and Citrix Certified Expert on Application Delivery and Security (CCE-AppDS).

He frequently works for Citrix international Consulting Services and several education centres all around the globe.

Johannes lives in Austria. He had been borne in Innsbruck, a small city (150.000 inhabitants) in the middle of the most beautiful Austrian mountains (https://www.youtube.com/watch?v=UvdF145Lf2I)

Add comment

By Johannes Norz

Recent Posts

Recent Comments