LDAP and Citrix ADC / NetScaler

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.

This is a trace done on my NetScaler.

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!

1st Packet (266), bindRequest(1)

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

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)

This is a simple success message. No more words needed here.searchRequest(2)

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

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)

For 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)

This 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)

This is just a message telling us: We had been lucky guessing this highly sophisticated password.

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!