PHP sessions, public directory and isolation

Alwaysdata, a hosting company, recently fixed a vulnerability. Indeed, they stored PHP sessions in a shared directory. This allowed an attacker to know PHP sessions ID, without their content, and in which account it has been used.

First of all, I would like to thank alwaysdata for their reactivity.

CVSS (Criticality metric)

CVSS: 6.3 (Medium)

  • Attack Vector: Local
  • Attack Complexity: Low
  • Privileges Required: Low
  • User Interaction: None
  • Scope: Changed
  • Confidentiality Impact: Low
  • Integrity Impact: Low
  • Availability Impact: Low

Vector: CVSS:3.0/AV:L/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:L

Timeline

Writeup

I was reviewing recently the PHP configuration of my alwaysdata account… and that is what I found:

session.save_path = /tmp

Ok, this means that all my sessions are stored in the /tmp directory.

But PHP is storing its sessions in a file, one per session, named as sess_<sessid>.

So:

  • If this directory is not a private directory, my session IDs are exposed.
  • If the umask of my PHP process is too permissive, everybody hosted in the same server can know, even modify in some case, the content of my PHP sessions.

But, with this hosting, the /tmp directory is shared (!!). Hopefully, the umask of the PHP process does not permit getting or writing the content of a session.

So we are able to get sessions ID! And the attack is too easy:

  1. Open an account for shared account (the free plan is enough).
  2. Open the SSH access.
  3. List session files in /tmp:
$ ls -l /tmp/ | grep sess_
total 16639
[...]
-rw-------  1 XXXXUSERNAMEXXX    XXXGROUPXXX      0 Sep XX XX:XX sess_fd863XXXXXXXXXXXXXXXXXXXXXXXXXXX
-rw-------  1 XXXXUSERNAMEXXX    XXXGROUPXXX    XXX Oct XX XX:XX sess_fdf86fXXXXXXXXXXXXXXXXXXXXXXXXXX
-rw-------  1 XXXXUSERNAMEXXX    XXXGROUPXXX      0 Sep XX XX:XX sess_fe6f60XXXXXXXXXXXXXXXXXXXXXXXXXX
-rw-------  1 XXXXUSERNAMEXXX    XXXGROUPXXX      0 Oct XX XX:XX sess_fe97c3XXXXXXXXXXXXXXXXXXXXXXXXXX
-rw-------  1 XXXXUSERNAMEXXX    XXXGROUPXXX      0 Oct XX XX:XX sess_fed3ebXXXXXXXXXXXXXXXXXXXXXXXXXX
-rw-------  1 XXXXUSERNAMEXXX    XXXGROUPXXX      0 Oct XX XX:XX sess_fedafeXXXXXXXXXXXXXXXXXXXXXXXXXX
-rw-------  1 XXXXUSERNAMEXXX    XXXGROUPXXX      0 Oct XX XX:XX sess_ffcb77XXXXXXXXXXXXXXXXXXXXXXXXXX
-rw-------  1 XXXXUSERNAMEXXX    XXXGROUPXXX    XXX Oct XX XX:XX sess_ffd804XXXXXXXXXXXXXXXXXXXXXXXXXX
  1. Use it. Ok, we don't have exact victims' URL, but it's easy to retrieve it: the (fictitious) user MichaelBlog must be a blog of somebody named "Michael" and hosted by alwaysdata. With this amount of information, search engines, as Bing, supporting the ip keyword can find URLs for us :-)

In order to optimize this attack, we can apply some strategies:

  1. Ignore empty sessions. Indeed, we have the size of session. An empty session has, probably, no content ;)
  2. Test recent session at the beginning. Indeed, we have the date of the creation of the session file. When a CMS/Framework/website is invalidating too old sessions, we have more chance to have a correct session ID with recent session files.

So this vulnerability allowed an attacker to get all session ID for some clients, to know what sessions have data and to know the age of sessions.

How to fix

There are two possibilities to fix this vulnerability.

Short-term fix (quick fix)

Changing the path where sessions are stored (define a private directory). This can be a directory in /tmp, but it must be readable and searchable only for the right system user (= mode 0700 for directories and mode 0600 for files).

Long-term fix

A way to fix it for real, anticipating some other potential problems, and anticipating a change in the configuration, is to isolate accounts. Indeed, currently, accounts isolated only by using another system user account, and this problem is an isolation issue. More, a strong isolation will prevent that an attacker, after gaining control of an account, can access information on another account. Strong isolation is, ultimately, the most reliable solution.

To do so, BSD developed a system of "jail", initially done for hosting services. The equivalent on Linux is namespaces. These technologies isolate some process to others. They are reliable. Indeed they are used and reviewed by several people and company. At the same time, they are used in several Google products (in Chrome/Chromium/ChromeOS sandboxing), or in ClipOS, the OS made by the French computer security agency "ANSSI".

Bad idea to fix it

One of pitfalls when trying to fix this vulnerability is to disable the listing for /tmp. Indeed, in this case, it's possible to retrieve sessions ID by listing session files with command ls or stat. Of course, we must make an exhaustive search and it takes too much time, but it must be feasible with luck or optimizations.

Proof of Concept (POC)

The code of the POC has been pushed on my Gitlab (https://gitlab.com/ajabep/poc-php-sessions-alwaysdata).

Flow of the POC is:

  1. Alice logs on to the authentication page.
  2. Eve runs the attack to get a session ID by listing sessions in /tmp.
  3. Eve changes her session cookie, and is authenticated as Alice.