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

[4] Prevent User Enumeration using WebAuthn #32021

Closed
wants to merge 1 commit into from
Closed

[4] Prevent User Enumeration using WebAuthn #32021

wants to merge 1 commit into from

Conversation

PhilETaylor
Copy link
Contributor

@PhilETaylor PhilETaylor commented Jan 12, 2021

  • On 18th June 2020 I reported this to the Joomla Security Strike Team.
  • I asked for an update today, and was told it was confirmed but no action has been taken
  • I have been repeatedly told that Joomla 4 has not been released and therefore the JSST are not interested in Joomla 4 security issues being reported in private and they are to be done in the public tracker.
  • I tried to explain the problem and the solution today, in brief, but the @zero-24 was not understanding

So here is the proposed fix.

The problem is, with WebAuthn, it is possible to Enumerate users that exist and have WebAuthn enabled by simply going to /administrator/index.php login page, providing a username and clicking "Web Authentication"

This makes an Ajax call to check if that user exists and if they have WebAuthn enabled.

If the username doesn't exist, if there is an exception thrown, or if the username exists and they don't have WebAuthn enabled, then the Ajax returns false and the user gets a message:

Screenshot 2021-01-12 at 23 03 57

if the username exists and the user has WebAuthn enabled then the Ajax return a challenge - which is a json string like this example below, and the browser prompts for the user to use their WebAuthn device (fingerprint, yubikey etc)

{"challenge":"-XGWsHC2uM52C5Rgu6uiDFGmxjQL9DnIf_7o3QxIqTo","rpId":"aec9d095825e.ngrok.io","userVerification":"preferred","allowCredentials":[{"type":"public-key","id":"OGJkYzAxMjQ4YWEwYjZjY2E5MWQ3ZGUyZjFmNTM2OGM1YWFiZTJiMGIwNTk1NTY5ZGViZDEwMTY3OTM3NmI0Yw"}],"timeout":60000}

Therefore it is possible to Enumerate users based on the response to the Ajax call. (and its possible, after obtaining the CSRF token, to send CURL requests to the Ajax endpoint millions of times a min with different usernames) to quickly check millions of usernames

Ironically, the users with WebAuthn enabled on their account are probably more security minded (power users, or with Super Admin rights) and therefore identifying their usernames is a nice thing for hackers... although probably useless, as the accounts are secured by webauthn - doh!

Summary of Changes

The result of the changes is that a challenge is returned from all attempts to use Web Authentication button to login to Joomla.

If the user has previously registered a WebAuthn device, then the challenge returned is a valid challenge and that user can go on to authenticate as normal by using their device.

if the username doesn't exist, or is a valid user without WebAuthn set up on their account, a challenge is returned that is valid enough for the browser to prompt for a WebAuthn device, but not valid enough that providing a device will authenticate the session. Its a fake challenge that prevents a Timing Vector and User Enumeration.

Testing Instructions

VISIT YOUR SITE OVER HTTPS

Enable WebAuthn on USERNAME-WITH-WEBAUTH
DONT Enable WebAuthn on USERNAME-WITHOUT-WEBAUTH
Logout.

On the login page enter username USERNAME-WITHOUT-WEBAUTH and click Web Authentication Button - you should be prompted to provide a WebAuthn Device press by your browser - press/use any webauthn device - note that you are rightly rejected access

Note that the fake challenge json is different based on the username provided and is indistinguishable from a genuine challenge.

Screenshot 2021-01-12 at 22 57 13

On the login page enter username USERNAME-WITH-WEBAUTH and click Web Authentication Button - you should be prompted to provide a WebAuthn Device press by your browser - press/use your registered webauthn device - note that you are rightly GRANTED access and the page reloads and you are logged in.

Documentation Changes Required

None

// Paging @nikosdion as the only one who might be able to review this on a technical level.

Signed-off-by: Phil E. Taylor <phil@phil-taylor.com>
@nikosdion
Copy link
Contributor

I am not sure if we can call this user enumeration or if it really matters. It does not list users and does not relay any other information about the user. You can only test whether a username is valid or not. In fact, not even that. You can only check if a username is BOTH valid AND has a WebAuthn identity attached to it.

I'd also like to point out that the current flow is compliant to the W3C Recommendation “Web Authentication:
An API for accessing Public Key Credentials Level 1”
§12.3. See point 2:

The script asks the client for an Authentication Assertion, providing as much information as possible to narrow the choice of acceptable credentials for the user. This can be obtained from the data that was stored locally after registration, or by other means such as prompting the user for a username.

Since we always want to allow users to log in from a different device than the one they registered their authenticator the final "or by other means such as prompting the user for a username" needs to be implemented. The side-effect is that it essentially allows you to determine if a username exists and which credentials it is attached to.

Further to that, a username seems to be a public, known identifier or at least everyone treat it as one. There plenty of extensions such as forum, helpdesk, comment, social networking, blog, some content enhancement etc which use the registered username as the publicly visible identifier for someone's words. In some cases username enumeration is a valid feature. Social networks are built around the concept that usernames are public. It's not the 1990s anymore.

Can anybody tell me what is the practical problem from knowing a username without knowing anything else about it including but not limited to real name, email, password, privileges etc? If there is a practical concern then the JED needs to remove all extensions which publicly use a username including forum, helpdesk, comment, social networking, blog, some content enhancement etc extensions. Wouldn't that be utter stupid in the age of social media and usernames as public identities?

More so when we are talking about the security minded people who will have a very hard to brute force password because, unless I'm mistaken, that was the only practical concern with knowing usernames: if the user has a weak password you have half of what is required to log in and the other half is easy to find.

Moreover, how is using the WebAuthn endpoint to guess usernames any different than throwing random usernames and passwords to the login endpoint once you scrape the token? The request response time is practically the same, give or a take a few dozen milliseconds which is nothing compared to the 200 to 2000 msec a request would take to process anyway. Doing an extra request to verify the username like that is pointless form an attacker's perspective.

In any case, if we want to be so facetious about security then, yes, this PR does achieve the stated goal... unless the attacker has more than half a working braincell. By definition, your fake authenticator identifier is an SHA-256 which is used by exactly zero real world authenticators. All real world authenticators I've seen use a 512-bit string which is base64-URL-encoded i.e. base64 with + and / replaced by - and _ respectively. It is therefore trivial for an attacker to look for this discrepancy to identify whether they have a correct username which also has WebAuthn enabled.

As this PR is right now I wouldn't accept it. It does not achieve the stated goal and I'm not even sure the stated goal does make any kind of practical sense. If we really want to treat usernames as secrets then the JED needs to unpublished half of the extensions that do anything useful. Sorry, I do not see any practical concern here.

@PhilETaylor

This comment was marked as abuse.

@brianteeman
Copy link
Contributor

The problem is that username enumeration will always occur on a registration form. There is no way to avoid that.

@PhilETaylor

This comment was marked as abuse.

@brianteeman
Copy link
Contributor

Correct. "This username is taken" is another example of User Enumeration.

Yes it is ;)
image

@PhilETaylor

This comment was marked as abuse.

@nikosdion
Copy link
Contributor

Your whole argument is based on your belief that the project should not address User Enumeration because usernames are now "public knowledge" in 2021 anyway.

No, it's not about "belief". A "belief" is something unsupported by facts and reality. Your saying that being able to verify if a username exists on a site is a Bad Thing is a belief because it is unsupported by facts and reality.

User Enumeration only makes sense as a tool to refine password brute force attacks. The idea is that if I know your username I can try to send different passwords (practically: known leaked passwords) to see if I can't hit a jackpot. Trying to send a deluge of common or plausible passwords to an endpoint to see if the username exists isn't the problem, especially when the endpoint will only give me a list of usernames which have WebAuthn enabled. These are the usernames I don't want to try attacking because they are far less likely to have a vulnerable password.

I don't care if this PR is implemented or not, I just don't think that it achieves the stated goal or that it's enough.

What I would much rather implement is an option in the WebAuthn plugin which PREVENTS user authentication with a password if WebAuthn is enabled. In this case it doesn't matter if I know who has WebAuthn enabled; I can't brute force their password.

Regarding the project's decision about User Enumeration, I think it's BS. If you want to completely prevent user enumeration you need to disallow self-registration (core or third party, including all e-commerce extensions!) and forbid any third party extension from showing a username in a publicly accessible page. This means you can't have e-commerce, forums, helpdesk, comment etc extensions. You end up with a static site generator. Is this really what the project wants or is the decision regarding user enumeration applied selectively? Neither makes sense.

In the end of the day usernames need to be treated as public identifiers and the effort being at preventing weak passwords which can be brute forced or, generally, any kind of passwords because they CAN be subverted. That's the whole point of implementing WebAuthn. If you want to commit suicide by removing WebAuthn because it will help you determine which users have chosen not to be hacked easily by weak and subverted passwords please go ahead. There are other ways to put sense back into the CMS, from third party extensions to forking depending on the severity of stupidity afflicting the project.

Do whatever, people. I am already working on a new version of WebAuthn which allows for client-side credentials, completely bypassing the problem of having to enter and validate the username. I am not contributing that back to the project because it'll never make it to 4.0 and I'm honestly done trying to reason about things that are obvious. I have better things to do with my life.

@PhilETaylor

This comment was marked as abuse.

@ceford
Copy link
Contributor

ceford commented Jan 25, 2021

Thank you for this discussion. I am too much of a security novice to comment or test myself. Maybe one way to learn is to watch and listen!


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/32021.

@zero-24
Copy link
Contributor

zero-24 commented Jan 25, 2021

Thank you for this discussion. I am too much of a security novice to comment or test myself. Maybe one way to learn is to watch and listen!

No need to have indepth security knowledge. The test is about checking whether webauth still works as expected after the pathc :)

@PhilETaylor

This comment was marked as abuse.

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

Successfully merging this pull request may close these issues.

None yet

6 participants