Joomla 3.8.8 Session deletion race condition

The Joomla Security Strike Team would like to share more details about the session deletion race condition vulnerability fixed in Joomla 3.8.8.

Introduction

The JSST has been informed about an occasional issue concerning a "Random logout failure" for the backend. The JSST ran an investigation and confirmed it. The problem was finding a proper patch, but let's start from the beginning.

Problem from the user’s point of view

When a user logs in into the backend and immediately logs out, it may happen that under some very rare circumstances the user's session becomes active again after a couple of seconds. It means that, even though the login screen is displayed correctly, providing a strong visual feedback of "everything worked fine" to the user, a simple refresh of the page would be enough to login the user again.
Technically, the user’s session resurrects as a zombie shortly after its destruction. From the user’s perspective, it’s as if the logout never happened.

What happens under the hood

What happens under that hood at this point?
You need to know two things at this point:

  • On the backend control panel there are two AJAX checks about extension and Joomla updates
  • These two AJAX requests run with your session and after they finish write the session data back to the session handler.

So what is happening here?
When you log in to the backend, several AJAX requests are started using your session information. When you log out of the site, Joomla resets the session data for the given session ID, logs the user out and then redirects them to the login form requesting a new session.
When logging out, if some AJAX requests are still active (such as the checks for core and extension updates), the cookie is not correctly destroyed, the browser keeps the session ID and Joomla creates a new session with the same session information as before.
When the AJAX processes end after you have logged out, these processes write their session data for the ID back into the session data store, overwriting the empty dataset you have just created on accessing the login page. When the user refreshes the page, they are logged in again.

In normal cases the AJAX requests just take a few seconds to finish and show you the result but, depending on the network connection, they could take longer and produce this kind of issue.

The following illustration should make it a bit more clear what is happening:

 
 Modified Image - Original Source: http://thwartedefforts.org

Real world scenario

Well, how can this become a security issue?
Think about a public (or someone else’s) computer that you use to log in your Joomla site and edit an article. Once you have finished, you go back to the Control Panel and then log out, taking you back to the login screen of your Joomla site. What now could happen is that you leave the place and someone else is coming to the machine, hitting refresh in the browser and getting logged in with your permissions to your backend. So they have gained unauthorised access to your site and can do whatever you could do on the site.

Our solution

Our solution is basically to make sure that when you logout not only the session is deleted from the storage engine, but also that the browser session cookie is cleared as well.
The race condition remains, but this approach makes it almost harmless, as the potentially still active session now can’t be accessed anymore, as the knowledge about the session ID required to use the session is lost.

Implications on some session handlers

We have two possible cases here:

  • Case A: Handler differentiates between "UPDATE" and "ADD" tasks
    Some handlers (database, memcache(d), redis and wincache) have dedicated commands to add a new or update an existing session. In this scenario, a long-running AJAX process would try to update the user session which will fail because the session in question has been deleted on logout.
  • Case B: Handler doesn’t differentiate
    Other handlers blindly create or overwrite the session (XCache, APC(u)). In this case, they will create an orphan session containing a clone of the data "before the logout" that nobody will use and that is going to expire soon.
    We have to highlight that in this second case, despite the logout actually works and prevents anyone else from reentering using the same browser, the user believes that their session has been destroyed but a shadow session could still exist on the server.
    This does not expose the user to a risk more than a regular backend activity as the knowledge about the session ID is required to use the shadow session.