I would like to report a privilege escalation vulnerability in flintcms.
It allows to reset a known user password, extract its password reset token and reset its password to then access the account.
module name: flintcmsversion:v.1.1.9npm page: https://www.npmjs.com/package/flintcms
Flint is a CMS built to be easy to use and super flexible. Your content needs to fit into more layouts and environments than anyone but you can plan for, so Flint enables you to make the templates you need and fill it with your content. It’s a CMS that is built for those who want to fully design the front-end of their website without wanting to deal with static site generators or older content management systems (that are slow and use outdated technology).
7 downloads in the last week
The vulnerability is caused by the lack of user input sanitization in the route that verifies the password reset token. The value from the parameter is directly sent to the Mongoose API which allows a user to insert MongoDB query operators. These operators can be used to extract the value of the field blindly in the same manner of a blind SQL injection. In this case, the $regex
operator is used to guess each character of the token from the start.
Vulnerable code:
router.get('/verify', async (req, res) => {
const token = req.query.t
const user = await User.findOne({ token })
if (!user) {
res.redirect('/admin')
return
}
res.redirect(`/admin/sp/${token}`)
})
You can tell the different behavior when visiting these pages (assuming one of the user has reset their password):
To take over an account, the following are required:
To lift the requirements to know the email, it is also possible to find the emails of the users because the password reset form is also vulnerable to blind MongoDB injection. In the same manner as previously, each character of the email can be guessed using the $regex
MongoDB operator.
Vulnerable code:
router.post('/forgotpassword', async (req, res) => {
const { email } = req.body
const user = await User.findOne({ email })
if (!user) {
res.status(400).end('There is no user with that email.')
return
}
// [...]
[email protected]
The request parameters should be converted to string before being sent to the Mongoose API. Adding .toString()
to the parameters should be enough to prevent objects being passed to Mongoose. For example:
const { email } = req.body
const user = await User.findOne({ email: email.toString() })
const token = req.query.t.toString()
Further sanitization should be added to other endpoints.
requests
packageAn attacker could take over the website, delete data or server malicious content.