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
(per this POC github: https://github.com/Vozec/CVE-2023-7028) emails = request.POST.getlist("email")
Django doesn't mix lists and single string values, because I'd been burned by that problem in PHP.But then using the email address from the request rather than the already verified address in the DB seems like a weird design decision to me anyway.
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.
If you changed it now you would break a whole lot of stuff.
I use it extensively in my own Datasette application, eg here: https://datasette.io/content/plugins?_facet=owner&_facet=is_...
If I had to guess what they did was:
user = User.find_by(email: params[:emails])
params[:emails].each { |email| send_recovery_email(user, email) }
Instead of: user = User.find_by(email: params[:emails])
send_recovery_email(user, user.email) if userEdit: Someone has digged it out: https://news.ycombinator.com/item?id=39162126
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.
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.
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...