Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

passwordless login! #37

Closed
weightyfoe opened this issue Aug 16, 2013 · 7 comments
Closed

passwordless login! #37

weightyfoe opened this issue Aug 16, 2013 · 7 comments
Assignees

Comments

@weightyfoe
Copy link

We've noticed that it's possible to login using adauth by supplying a correct username & a blank password. We've seen the following:

correct username & correct password -> login
correct username & incorrect password -> fail
correct username, & blank password -> login (as username)
incorrect username & [correct|incorrect|blank] password -> fail

This is even for users who are NOT currently in the users table (ie have never previously logged in).

Currently using the latest git master branch (pulled from git today using bundle update):
https://github.com/Arcath/Adauth.git

Setup is like this (boring bits omitted):

initializers/adauth.rb
c.doman = "domain.local"
c.query_user = "<a low privilege account>"
c.query_password = "****"
c.server = "10.0.0.1"
c.base = "dc=domain, dc=local"
models/user.rb
class User < ActiveRecord::Base

include Adauth::Rails::ModelBridge

AdauthMappings = {
    :login => :login,
    :group_strings => :cn_groups,
    :ou_strings => :dn_ous,
    :email => :email,
    :name => :name
}

AdauthSearchField = [:login, :login]
controllers/sessions_controller.rb

def create
   begin
        ldap_user = Adauth.authenticate(params[:username], params[:password])
        if ldap_user
            user = User.return_and_create_from_adauth(ldap_user)
            session[:user_id] = user.id
            redirect_to <somewhere nice> and return
        else
            redirect_to <a bad place> and return
        end
    rescue
        redirect_to <an even worse place> and return
    end
end

def new
    redirect_to root_path if current_user
end

We added the begin/rescue/end in the create session because we encountered an error when giving incorrect usernames/passwords (ldap_user was true even for bad usernames - like logging in as asdf/crap_pass)

Removing the rescue still lets you log in without a password, and errors if the username or password is incorrect (but not blank), ie it being there has no effect on this issue, but it does prevent an error.

What's going on?

Oh, an excerpt from the users table:

id: 18
login: james
group_strings: --- - !ruby/string:Net::BER::BerIdentifiedString |-   U1NMIFZQTiBVc2Vycw== - !ruby/string:Net::BER::BerIdentifiedString |-   QXBwX1NoYXJlcG9pbnRfS25vd2xlZGdlVXBkYXRlcnM= - !ruby/string:Net::BER::BerIdentifiedString |-   RGVwdF9JVA== - !ruby/string:Net::BER::BerIdentifiedString |-   RXZlcnlvbmUgT3V0bG9vaw== - !ruby/string:Net::BER::BerIdentifiedString |-   RG9tYWluIEFkbWlucw== - !ruby/string:Net::BER::BerIdentifiedString |-   RW50ZXJwcmlzZSBBZG1pbnM= 
name: James
ou_strings: --- - Users - IT - Department - CompanyHO 
email: james@domain.com

Not sure whats going on with group_strings(!) or ou_strings, but I don't currently use them for anything.
This user logged on by providing username "james" and no password

@ghost ghost assigned Arcath Aug 16, 2013
@Arcath
Copy link
Owner

Arcath commented Aug 16, 2013

This is very odd.

I threw a quick test together which looks like this:

ldap_user = Adauth.authenticate("administrator", "")
ldap_user.should be_false

which passed no problem. then after adding a few more cases to to the test that first example failed giving me a successful authentication as administrator with no code change or anything...

Something very odd is going on here its almost as if AD is allowing password less bindings which as far as I'm aware it shouldn't.

More research is needed

@weightyfoe
Copy link
Author

Is there anything I can do over here to provide some more data?
AD config variables, run some ruby code?

You'll have to be be precise about what you want me to do though - I don't really know much at all about AD & it's configuration. Glad to help though, especially if it is something our AD is doing wrong (passwordless logins! Sounds bad...)

@weightyfoe
Copy link
Author

Here's the log of a single login attempt, using username "james" and no password:

# Logfile created on 2013-08-16 11:58:31 +0100 by logger.rb/36483
Loading new config
Attempting to authenticate as james
Searching for all "(objectClass=user)" where sAMAccountName = james
Connecting to AD as "low_priv_user"
Searching for all "(objectClass=group)" where name = SSL VPN Users
Searching for all "(objectClass=user)" where sAMAccountName = 
Searching for all "(objectClass=group)" where sAMAccountName = 
Searching for all "(objectClass=user)" where sAMAccountName = ITIPAD
Searching for all "(objectClass=group)" where sAMAccountName = ITIPAD
Searching for all "(objectClass=group)" where name = App_Sharepoint_KnowledgeUpdaters
Searching for all "(objectClass=user)" where sAMAccountName = 
Searching for all "(objectClass=group)" where sAMAccountName = 
Searching for all "(objectClass=user)" where sAMAccountName = James Bhatt
Searching for all "(objectClass=group)" where sAMAccountName = James Bhatt
Searching for all "(objectClass=group)" where name = Dept_IT
Searching for all "(objectClass=user)" where sAMAccountName = 
Searching for all "(objectClass=group)" where sAMAccountName = 
Searching for all "(objectClass=user)" where sAMAccountName = User1
Searching for all "(objectClass=group)" where sAMAccountName = User1
Searching for all "(objectClass=group)" where name = Everyone Outlook
Searching for all "(objectClass=user)" where sAMAccountName = 
Searching for all "(objectClass=group)" where sAMAccountName = 
Searching for all "(objectClass=user)" where sAMAccountName = User1
Searching for all "(objectClass=group)" where sAMAccountName = User1
Searching for all "(objectClass=group)" where name = Domain Admins
Searching for all "(objectClass=user)" where sAMAccountName = 
Searching for all "(objectClass=group)" where sAMAccountName = 
Searching for all "(objectClass=user)" where sAMAccountName = SQLEngine Original
Searching for all "(objectClass=group)" where sAMAccountName = SQLEngine Original
Searching for all "(objectClass=group)" where name = Enterprise Admins
Searching for all "(objectClass=user)" where sAMAccountName = 
Searching for all "(objectClass=group)" where sAMAccountName = 
Searching for all "(objectClass=user)" where sAMAccountName = User2
Searching for all "(objectClass=group)" where sAMAccountName = User2
Authentication succesful

I've left my name in, but bodged out the others & replaced them with generic peeps.

@weightyfoe
Copy link
Author

Just a question.

Is the binding bit done by this user (set in adauth.rb)?

c.query_user = "<a low privilege account>"
c.query_password = "****"

Because this user is a real user on our AD, ie I would be able to log in as this user & be authenticated.
Or, does it use the username & password provided on the login page?

Because in the sessions controller I use (as per the doc):

ldap_user = Adauth.authenticate(params[:username], params[:password])

Which would be the username & password that are typed into the login form correct?
So what is the user defined in adauth.rb for?

@weightyfoe
Copy link
Author

Ok,

So I create a brand new AD account (called special). The account is bog standard: primary group is Active Directory/users

I can still "Authenticate" a user without using a password, if I connect using the above account as the query user.

I'm unsure of the internals of how AD/LDAP queries actually work, but could this possibly be something like:

  1. Connect as a given user (a query user used to make all queries against AD) to create a "connection" to AD
  2. Using that connection, ask AD: "Does a user with user name 'foo' and password '' exist?"
  3. AD seems to allow an authenticated user to query the directory, so it says yes, there is a user whose username = 'foo' (and because no password is specified, AD doesn't check it)
    3a) Same query, but this time using a bad password: Ad says no, because although the user 'foo' exists, the password hashes (which it now uses because it is specified) do not match.

So, is it a case the because we're already connected to AD using the query_user, we're not "authenticating" the user who is trying to log in, we're just trying to see whether they exist or not?

And, could this be fixed by running the query using the supplied username & password rather than using a defined query_user?

@Arcath
Copy link
Owner

Arcath commented Aug 28, 2013

Query User us used to let Adauth query for information e.g. find the groups user X is in.

Authenticate creates a new connection to AD using the supplied credentials to make sure that the details are valid. As far as I'm aware the only way Adauth would do this is if Net/LDAP returned a valid connection and again that should only happen if it gets a successful connection

I've got some time today to test this so hopefully a solution will exist soon (Now that I've managed to replicate the issue my boss is happy for me to spend time looking at instead of doing other work)

@Arcath Arcath closed this as completed in 27b0b5a Aug 28, 2013
@Arcath
Copy link
Owner

Arcath commented Aug 28, 2013

I've written up the case here:

http://arcath.github.io/blog/2013/08/28/issue-37/

Basically the LDAPv3 Specification states that you need to make the rootDSE accessible anonymously which because we bind against it meant that Adauth would get a false positive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants