Unauthenticated Session Fixation Attacks

Since modern authentication frameworks (like JAAS in combination with current JavaEE application servers) try to mitigate the Session Fixation attack scenario out-of-the-box, one might assume that this attack vector is mostly relevant for custom developed login schemes. Unfortunately during my pentests of applications, which properly change the session identifier upon login, I still find Session Fixation attack scenarios. These often arise from the misconception that the login process is the only workflow of an application that adds (from an attacker's point of view) significant value to a shared anonymous session.

In this article I showcase typical scenarios regularly found during pentests where unauthenticated Session Fixation attacks occur and how they can be exploited by targeting application workflows aside from the login process.

Session Fixation exploitations without login

First of all, this is nothing really new: The attack scenario of Session Fixation is well understood and lots of good documentation is available (for example at OWASP or the excellent article at Wikipedia). At least one paragraph of the Wikipedia article suggests that the Session Fixation attack scenario is not limited to applications that have logins or are otherwise protected by some authentication scheme. But almost all examples of the attack's exploitation scenarios available online showcase the victim's login using a fixated session identifier, making the attacker being logged in as the victim too.

In the last few years many authentication frameworks have incorporated automatic renewal of session identifiers into their login workflows to stop this attack scenario:

  • For example JavaEE applications running on current web application servers and using authentication frameworks like JAAS often automatically get their session identifiers renewed upon login.
  • Other applications (sometimes protected by single-sign-on components) issue a separate second session identifier when the user is "logged in".
This kind of "out-of-the-box protection" delegated to the authentication frameworks might suggest to some developers that the Session Fixation problem has been addressed sufficiently.

On the other side it is embarrassing to see how many applications manage to successfully renew their session identifiers at authentication boundaries, but otherwise still rely on a non-changing session identifier in public forms and wizards where users may enter sensitive data. Therefore I hope to put some focus on the topic of Session Fixation in unauthenticated application workflows with this article.

From my experience as a pentester here are some scenarios where Session Fixation attacks are still a valid vulnerability in application workflows without any authentication boundaries being passed by the victim:

User registration wizards
Often sites where users can create their own accounts (afterwards protected by an authentication boundary of course) have a public self-registration wizard which asks the user to enter personal data into a multi-step form, which is often backed on the server-side by a web session. Sometimes these wizards have custom links or buttons to go to the previous or next step. Other wizards might have a final confirmation page at the end where the user has to confirm the entered data. All these are points of the workflow where the data entered by the user can be seen again allowing either for correction or confirmation. These points of printing out the session's user data allow attackers to retrieve the sensitive data entered by a victim with a fixated session. To make it worse: some broken user registration wizards let the freshly created account be used directly by the session from which it was created without any authentication (since the user just created it).

It is obvious that in all these scenarios no authentication boundary (like a login) was passed by the victim, so these scenarios are a blind spot for the automatic session renewal of authentication frameworks that only trigger upon the login action: The attacker would fixate the session to the victim as usual in Session Fixation attacks and wait until someone enters sensitive data in the registration wizard. When this is happening the attacker (with the shared anonymous session) sees the data entered by the victim by refreshing the confirmation or edit page of the wizard.

Shopping carts
Another similar scenario are shopping carts in the unauthenticated parts of an online shop. Most (if not all) web shops allow to fill the shopping cart prior to logging in. The exploitation scenario is at first sight a privacy issue, since the attacker can read what products the victim searches for and places in the shopping cart of a fixated session. But also an abuse of the checkout process is possible with an unauthenticated Session Fixation attack when the shop has an optional "purchase without registration" feature that doesn't require customers to create an account and login with the online shop.

Again: No authentication boundary (login) was passed by the victim so no automatic session renewal of an authentication framework will trigger during the shopping cart usage or the account-less checkout process.

Contact forms
Different scenario, same story: Many sites have contact forms where users can ask support questions. Often these contact forms are available even in the unauthenticated parts of the web application. Some of them consist of at least two steps with a final confirmation page showing what will be sent to the support team allowing the user to eventually go back and adjust the message. Even some contact forms ask questions like customer number, full name, contact details etc., which might be of interest to attackers to conduct further social engineering attacks.

As you can imagine: Attackers can read these data of victims that use a fixated session to fill out the contact form and no login was part of the game, so no automatic session renewal.

Account fixation
That's an interesting scenario: It is described in the Wikipedia article on Session Fixation in the paragraph suggesting that Session Fixation scenarios are not only tied to victims logging in. Here the attacker authenticates itself with the targeted application and lets the session-fixated victim use it in an "already logged-in to attacker's account" state, allowing the attacker to see what the victim is entering inside the authenticated part of the application (via the account's purchase or payment history for example). To be precise, this scenario relates to the authenticated part of an application but nicely evades the "session renewal upon login" protection by letting the attacker fixate the already authenticated session identifier to the victim. From an exploitation point of view, this scenario can be seen as similar to Login-CSRF where an attacker authenticates the victim unwillingly with the attacker's account, allowing the attacker to read sensitive data entered by the victim in the authenticated context.

This scenario demonstrates the requirement for session renewals inside the authenticated parts of an application after the login has already been performed. Relying on authentication frameworks to handle Session Fixation during the login process again fails at protecting against this scenario.

As you have seen and surely can imagine: Many scenarios exist inside a typical web application where Session Fixation attacks can be performed with valid exploitation scenarios without the victim passing any login.

Defining "Session Renewal Points"

Often one reads that renewing the session identifier upon login solves the classic Session Fixation problem and that renewing it on every request is only an optional countermeasure to further harden the application. From a developer's point of view renewing the session on every single request is sometimes not feasible, depending on the application's requests and users performing multi-tab usage.

It clearly depends on the inner value of the web session and at what points of the workflow this value increases: The most important increase of the inner value of an (attacker shared) session identifier is passing any authentication boundaries (like logging in). But developers (and pentesters) have to carefully find points in the application workflow where other types of value increase of the sessions happen, like when users enter sensitive data into a private web session that will be printed out at some other part of the application (either the next wizard page or somewhere completely different).

So when renewing the session identifier on every request is too difficult for the application to perform, one has to renew the identifier (in addition to the obvious login process) at least at selected points derived from dataflow analysis of the users' data through each usecase – like final wizard pages printing out entered sensitive data or between wizard pages when back-navigation is possible.

Secondary countermeasures

Failure to identify all relevant session renewal points of an application as a developer during threat modeling leads to the fact that (as always with defense in depth) some secondary countermeasures might hinder successful exploitations nevertheless. Here are some thoughts on what can be done to further harden an application regarding this topic:

Cookie-only session tracking
Not accepting session identifiers from URLs (or request parameters in general) is always a good practice: In Java web applications this can be achieved using the Servlet 3.0 web.xml <session-config> flag <tracking-mode>COOKIE</tracking-mode>. Cookie-only session tracking does not stop successful Session Fixation exploitations completely, but it reduces the attack surface somewhat:
  • Now an attacker has to either use a second vulnerability (like browser bugs) to perform Cross-Site Cooking or relies on Header Injections or XSS (JavaScript or Meta-Tag) to fixate the session identifier as a cookie to the victim. This is sometimes easier for an attacker when distinct parts of an application with different security requirements reside only on different subdomains instead of different domains (like user content being served from the same main domain only separated by subdomain), which leads to Cross-Subdomain Cooking.
  • Another scenario to exploit Session Fixation even with Cookie-only session tracking in place is the Cookie Forcing technique: Here a series of redirects issued by Man-in-the-Middle (MitM) attackers while intercepting HTTP traffic unrelated to the application can even set/overwrite cookies for the HTTPS-only web application to attack, allowing secure-flagged session cookies to be overwritten (effectively fixating the session identifier) – while at the same time leaving all HTTPS traffic untouched (SSL pass-through) as the MitM attacker. The idea of Cookie Forcing is to at least write Cookies (even on HTTPS protected connections) similar to Surf Jacking, which could be used to read unprotected Cookies. This scenario requires the attacker to be closer to the victims (in terms of network access) as opposed to the classic "spam or socially share a link" scenario of Session Fixation with prepared URLs. But since attackers can set the malicious cookie (fixating the session identifier) even as a persistent cookie, the window of opportunity for Session Fixation exploitation through Cookie Forcing is greater than one might assume. Also note that usage of a proper HSTS header (HTTP Strict Transport Security) somewhat fixes this flaw in cookie design, since with HSTS the browser would not issue HTTP requests to the HTTPS-only targeted application after following a redirect from other unrelated (intercepted) HTTP traffic.
Session binding to client properties
Binding the web session to some client specific values (like IP address where possible or the User-Agent) is always a good thing. When binding the session to the User-Agent header, an attacker needs either a second vulnerability to read the User-Agent header from the victim or it must be guessed. Since the set of unique User-Agent strings is not that big (assuming browsers tend to auto-update themselves nowadays), the session must be invalidated on the first mismatching attempt (i.e. the first request with a changing User-Agent must invalidate the session). Since some browsers legitimately change the User-Agent string while surfing (rotating mobile devices, version updates while surfing, compatibility modes, etc.) one could use the Levenshtein distance when comparing two User-Agent header strings to only treat greater differences as a mismatch.
Lenient referrer checks
Since spoofing referrer headers is quite easy, they are a double-edged topic in terms of security. But using a referrer check to further harden the application by discarding requests with unexpected referrers on non-entry points is a good thing. Since the victim would certainly not spoof the referrer header, an unexpected one could be some sign of strange things happening. But keep in mind that HTTP/HTTPS scheme changing requests as well as privacy plugins of browsers might remove the optional "Referer" header, so a request without any such header cannot be treated as unexpected.
Absolute session timeouts
In addition to the established inactivity timeouts of web sessions, setting an absolute session timeout to the maximum reasonable usage time of the application is very helpful. Attackers would keep sessions open as long as possible while their Session Fixation attacks (via spam campaigns for example) are running. Reducing the window of attack from days to a few hours is definitively helpful. If the application server does not have a configuration setting for an absolute session timeout, this can easily be checked with intercepting code like Servlet Filters or similar.