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
- 2018-10-10 (00:27): Initialize communication stream with alwaysdata.
- 2018-10-11 (17:40): Reporting vulnerability to alwaysdata.
- 2018-10-11 (18:34): Alwaysdata offers me a bounty.
- From 2018-10-15 to 2018-10-23: Alwaysdata deployed the fix.
- 2018-10-24 (00:40): Announce by alwaysdata in their changelog (https://changelog.alwaysdata.com/94/detail/) and on their twitter (https://twitter.com/alwaysdata/status/1055000974220296194).
- 2018-11-05 (11:00): Disclosing this vulnerability by this blog post.
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:
- Open an account for shared account (the free plan is enough).
- Open the SSH access.
- 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
- 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:
- Ignore empty sessions. Indeed, we have the size of session. An empty session has, probably, no content ;)
- 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:
- Alice logs on to the authentication page.
- Eve runs the attack to get a session ID by listing sessions in
/tmp
. - Eve changes her session cookie, and is authenticated as Alice.