Skip to content

Blind LDAP injection in login allows the attacker to leak arbitrary attributes from LDAP database

High
Gargron published GHSA-38g9-pfm9-gfqv Apr 4, 2023

Package

No package listed

Affected versions

>= 2.5.0

Patched versions

4.1.2, 4.0.4, 3.5.8

Description

Summary

Mastodon allows configuration of LDAP for authentication. The LDAP query made during login is insecure and the attacker can perform LDAP injection attack to leak arbitrary attributes from LDAP database.

Details

These are the default variables initiated if LDAP is configured:

if ENV['LDAP_ENABLED'] == 'true'
config.ldap_authentication = true
config.check_at_sign = true
config.ldap_host = ENV.fetch('LDAP_HOST', 'localhost')
config.ldap_port = ENV.fetch('LDAP_PORT', 389).to_i
config.ldap_method = ENV.fetch('LDAP_METHOD', :simple_tls).to_sym
config.ldap_base = ENV.fetch('LDAP_BASE')
config.ldap_bind_dn = ENV.fetch('LDAP_BIND_DN')
config.ldap_password = ENV.fetch('LDAP_PASSWORD')
config.ldap_uid = ENV.fetch('LDAP_UID', 'cn')
config.ldap_mail = ENV.fetch('LDAP_MAIL', 'mail')
config.ldap_tls_no_verify = ENV['LDAP_TLS_NO_VERIFY'] == 'true'
config.ldap_search_filter = ENV.fetch('LDAP_SEARCH_FILTER', '(|(%{uid}=%{email})(%{mail}=%{email}))')
config.ldap_uid_conversion_enabled = ENV['LDAP_UID_CONVERSION_ENABLED'] == 'true'
config.ldap_uid_conversion_search = ENV.fetch('LDAP_UID_CONVERSION_SEARCH', '.,- ')
config.ldap_uid_conversion_replace = ENV.fetch('LDAP_UID_CONVERSION_REPLACE', '_')
end

Then, during the authentication, this line is executed:

def authenticate_with_ldap(params = {})
ldap = Net::LDAP.new(ldap_options)
filter = format(Devise.ldap_search_filter, uid: Devise.ldap_uid, mail: Devise.ldap_mail, email: params[:email])
if (user_info = ldap.bind_as(base: Devise.ldap_base, filter: filter, password: params[:password]))
ldap_get_user(user_info.first)
end
end

So this query is filled with untrusted input, namely user's login:

(|(cn=%{email})(mail=%{email}))

User can inject LDAP query here. I didn't find a way to login as arbitrary user because there are two queries - one for fetching the user object and the second one for authentication and the injection exists in the first one.

However, the attacker can use blind injection technique to exfiltrate one bit of information at a time.

PoC

  1. Set up Mastodon and LDAP authentication
  2. Create a user admin with any password. The information that we want to leak is the description=LDAP Administrator attribute but it can be any (I'm not yet able to leak password hash but I'm working on it).
  3. Now, as the attacker, create a new account. In my case it's adminmalicious and password test.
  4. Now when we try to log in with the login adminmalicious)(&(cn=admin)(description=A*) and password test. The full query will look like this: (|(cn=adminmalicious)(&(cn=admin)(description=A*))(mail=adminmalicious)(&(cn=admin)(description=A*))). In plain english this query means "If the description of the user admin starts with A, the query will return both admin and adminmalicious". In my example, the admin description does not start with A so only adminmalicious will be returned from the LDAP query and we'll be logged in as adminmalicious.
  5. When we try to log in with adminmalicious)(&(cn=admin)(description=L*), the query will return both admin and adminmalicious but admin will be first and the app will try to login to user admin with the password test which will fail and we will see Invalid E-mail address or password..

Impact

This way we've leaked one bit of information of other user from the database. Of course, this is easy to script and leak whole attribute values.

Severity

High
7.7
/ 10

CVSS base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Changed
Confidentiality
High
Integrity
None
Availability
None
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N

CVE ID

CVE-2023-28853

Weaknesses

Credits