Update: as it turns out, Immunity is NOT using OpenCFP for their Infiltrate conference, even though the URL hints at it (https://opencfp.immunityinc.com)



OpenCFP is an open source conference talk submission system written in PHP. It is used by lots of conferences, including for example Immunity’s Infiltrate conference and others. This is a short write-up of a bug that I found while auditing OpenCFP, although the issue actually resides in a third-party auth framework used by OpenCFP called Sentry which is developed by Cartalyst. Despite being deprecated, Sentry appears to be quite popular so this bug is very likely to affect a number of other applications too.

The function



Like many applications, OpenCFP has a “forgotten password” function that allows a user to have a link emailed to them that can be used to set a new password. Following the link leads to a page that looks like this:

When clicking “Change my password”, a POST request like the following is sent to the server:

POST /updatepassword HTTP/1.1

Host: domain

Referer: http://domain/reset/2/vab9HtPIfIw2f6WLrzzidEbApaepDSGm9PkUKyKvZr

[...]



reset[_token]=x&reset[password][password]=passw0rd&reset[password][password2]=passw0rd&reset[user_id]=2&reset[reset_code]=vab9HtPIfIw2f6WLrzzidEbApaepDSGm9PkUKyKvZr

Once the application receives this request, it will check that the value of the reset[reset_code] parameter matches the value of the reset_password_code column for the specified user in the database. OpenCFP uses Sentry to perform this check as well as all other things related to authentication and authorization.

The bug

The issue is a combination of how Sentry performs this check, and the fact that users that do not have a password reset token stored in the database will by default have NULL in the reset_password_code column, according to Sentry’s database schema.

Except for some basic input sanitization, no further validation of the input is performed by OpenCFP before passing it to Sentry. The Sentry manual entry for the function performing the check does not provide any advice about disallowing certain types of input either.

Below is the function in Sentry responsible for checking whether the reset code in the request is the same as the one stored in the database. It is a simple function that returns true (for a match) or false.

This function is called by the attemptResetPassword() function. Note that the user’s reset code is restored to NULL as part of the password change.

The bug itself is also extremely simple. The issue lies in that if a value of NULL can be passed to the checkResetPasswordCode() function, it will return true for any user that does not have a reset token in the database. This can be accomplished by sending a request with the URL NULL character (%00), or even nothing at all, as the value of the reset[reset_code] parameter.

POST /updatepassword HTTP/1.1

Host: domain

Referer: http://domain/reset/2/vab9HtPIfIw2f6WLrzzidEbApaepDSGm9PkUKyKvZr

[...]



reset[_token]=x&reset[password][password]=passw0rd&reset[password][password2]=passw0rd&reset[user_id]=2&reset[reset_code]=%00

This will result in a successful password change.

This vulnerability can be used to set any OpenCFP user’s password, assuming that they don’t have an unused password reset token in the database. It should be noted that an attacker would still have to connect the numeric user ID to an email address to be able to log in, but that will probably not be very difficult considering that users 1-5 are likely to be the organizers of the conference.



The end

It should again be noted that other applications that use Sentry will likely also be affected by this issue, especially as the “forgotten password” functionality is part of the standard feature set for most web applications. If you know of someone who uses Sentry, please advice them to update and/or check how they handle input to this functionality.

This issue has been fixed in the latest version of OpenCFP, and the maintainers have also sent a patch to Sentry which has been merged as of September 5th.

Timeline

2016-08-31: Bug found and reported to OpenCFP



2016-08-31: OpenCFP issues a patch



2016-08-31: Dustin of OpenCFP submits a patch to Sentry



2016-09-05 - Sentry accepts and merges the patch



Kudos to Dustin and Chris of OpenCFP for their speedy and supportive handling of this issue.

