I'm guessing here, because I only read a high-level description of it, but I think it's a password reset flow endpoint that takes the email address to look up and send a reset to, and the framework will accept an array instead of a simple string; the endpoint looks up the first address, but the variable used to determine who to send the reset mail to is the array. Again: just a guess as to the underlying bug (I've seen that specific bug before is why I guessed).
The vulnerability lies in the management of emails when resetting passwords. An attacker can provide 2 emails and the reset code will be sent to both. It is therefore possible to provide the e-mail address of the target account as well as that of the attacker, and to reset the administrator password.
Here's an example payload:
user[email][]=my.target@example.com&user[email][]=hacker@evil.com
Strong parameters has been a core security feature of rails for a long time, and all the guides go into detail about the boilerplate you need just to accept a form input past the strong parameter filters. It's weird to me that the pattern doesn't also include a "must he a string" option. I know you can add to_s everywhere, but making it part of the existing strong params would actually incentivise use.
It's weird, as I recall with strong params one of the only things it really makes you decide is whether a value is a scalar, array, or Hash. You can certainly allow a value to be scalar or array, but it's not very natural in strong params.
I mostly hate the way strong params gets used - it's a bad compromise between letting Ruby people do Ruby things and trying to plug up a category of vulnerability that's been biting rails apps for a decade. Now I do all my api definitions in openapi and it's way easier. I haven't tried it with a rails app but I think it'd work well there.
This is such a rarely used feature that I wonder if it would be helpful to have a CSP or preflight header that restricts the browser from sending multiple values for the same parameter.
It's a great feature that's been supported by browsers for decades. <select multiple> uses it. You can use it for checkboxes to select multiple items too.
If you changed it now you would break a whole lot of stuff.
You wouldn't need to change it. But if you made it a CORS header like Access-Control-Allow-Headers, then websites would be able to provide a default policy forbidding it so that their code that actually requires it would need to explicitly opt into the behavior.
There is precedent for deputizing the browser to stop this kind of bug with Access-Control-Allow-Headers. If the backend wants to default to ignoring multiple GET/POST parameters with the same name, then the browser could helpfully fail to make a request that attempts to send them.
The attacker doesnt’t use a compliant browser to make the request. User agent protections only help in situations where a regular user (or their software) is being tricked
Properly used white list parameter controls (i.e., strong parameters) that are the default Rails behavior at this point would have prevented this bug completely.
This is a little like saying the best way to avoid this bug is to not have the bug. But that's true of all bugs. The C apologists used to say, "just bounds check properly!"
Content Security Policy is a User-Agent feature. The vulnerability here is server-side. A malicious actor exploiting this will be using their own HTTP client that does not respect a CSP.
I think I'd count that as a bug and a design error.
The bug is accepting an array when it should only take a scalar.
The design error is that the endpoint should not be taking email addresses at all. It should take account IDs.
Even if a system uses email addresses as account IDs they are conceptually not the same and the code should not muddle them.
Keep them separate and then even if you get an "allows an array where it should have been a scalar" bug the result should be either just the first account in the array gets a reset email or all the accounts in the array that are existing accounts get reset emails for their accounts.
If they have to allow lookup by email I don't understand why they wouldn't throw out the input data. They should only ever have had a function to send password reset that takes a user ID and uses the email on record from the database.
I think it is OK to call the account ID "Email Address" on everything the user sees, and make it the same string as the user's email address. As far as the user is concerned they login with email address and password.
I'm just saying that in the software and in the database store account ID and email separately. Treat the fact that the account ID column matches the email address column as just a coincidence that you do not take advantage of.
I'd enforce not taking advantage of it by having employee accounts actually use an account ID that does not match their email address, such as their name, so that if we accidentally leave out a call to EmailFromAccountID(...) somewhere and try to use an account ID directly as an email address it will break employee accounts.
Also, it is not clear to me that even with user visible account ID that is not the same as email address that it would take two email rounds trips.
The reset page could take email address, not account ID. The reset endpoint could then look up the account ID from the email address, and initiate the reset, calling the SendEmailToAccount service with the account ID to send the email. That service would look up the email address for the account.
Oh, sure. Storing the email address in a canonical column and using the provided address as a key helps. But I think the underlying bug is still there, because the email code will still _accept_ the user input if you feed that to it.
Personally, I like when people have usernames, and have to enter those, to receive a recovery message sent to the associated email.
Or better yet, enter both username and email together.
Because it's more likely the attacker won't know both.
In any event, I have been recommending to everyone for years to use email aliases (that GMail and others support) as your login. Have a different one for each site, for example yourname+az@gmail.com for amazon. That way, you can avoid crap like this which is out of your control, since the attacker won't even be able to repeat your login email: https://www.wired.com/2012/08/apple-amazon-mat-honan-hacking...