Bug 1080919 - (CVE-2018-6954) VUL-0: CVE-2018-6954: systemd: systemd-tmpfiles mishandles symlinks present in non-terminal path components, which allows local users to obtain ownership of arbitrary files
(CVE-2018-6954)
VUL-0: CVE-2018-6954: systemd: systemd-tmpfiles mishandles symlinks present i...
Status: RESOLVED FIXED
Classification: Novell Products
Product: SUSE Security Incidents
Classification: Novell Products
Component: Incidents
unspecified
Other Other
: P2 - High : Major
: ---
Assigned To: Security Team bot
Security Team bot
https://smash.suse.de/issue/199947/
CVSSv3:SUSE:CVE-2018-6954:7.1:(AV:L/A...
:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2018-02-14 07:51 UTC by Karol Babioch
Modified: 2020-04-29 09:09 UTC (History)
12 users (show)

See Also:
Found By: Security Response Team
Services Priority:
Business Priority:
Blocker: ---
Marketing QA Status: ---
IT Deployment: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Karol Babioch 2018-02-14 07:51:37 UTC
CVE-2018-6954

systemd-tmpfiles in systemd through 237 mishandles symlinks present in
non-terminal path components, which allows local users to obtain ownership of
arbitrary files via vectors involving creation of a directory and a file under
that directory, and later replacing that directory with a symlink. This occurs
even if the fs.protected_symlinks sysctl is turned on.

References:
https://bugzilla.redhat.com/show_bug.cgi?id=1545017
http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2018-6954
http://people.canonical.com/~ubuntu-security/cve/2018/CVE-2018-6954.html
http://www.cvedetails.com/cve/CVE-2018-6954/
https://github.com/systemd/systemd/issues/7986
Comment 1 Karol Babioch 2018-02-14 07:51:51 UTC
Upstream discovery and discussion: https://github.com/systemd/systemd/issues/7986
Comment 2 Franck Bui 2018-02-19 08:46:39 UTC
I've proposed the following fix https://github.com/systemd/systemd/issues/7986#issuecomment-366621458

Let's see where it goes...
Comment 3 Franck Bui 2018-03-09 10:35:16 UTC
OK the pull-request have been merged but the changes are quite big...

I'm not sure it worths to backport them though and taking the risk to get regressions.

Secteam, what do you think ? should we take the risk to fix this in SLE12/SLE15 ?
Comment 4 Matthias Gerstner 2018-03-09 12:39:14 UTC
Hey Franck,

good work with this pull request.

I think for SLE15 we should take the risk, since it is still under testing anyhow.

For SLE12 we should be careful. This is still quite a severe security issue so we should do something about it. I suppose there is no "simpler but still sufficiently effective" alternative to the upstream change. So we should invest into more intense testing.

Maybe you could devise a suitable test case for systemd on SLE-12 that covers the tmpfiles logic?

I could also offer a source code review of the backport to SLE-12 when it is ready.
Comment 5 Johannes Segitz 2018-04-10 10:57:43 UTC
I concur, we need to fix this on SLE 12 also. Can you please prepare submissions?
Comment 6 Franck Bui 2018-04-11 06:43:30 UTC
The problem is that the issue is not entirely fixed, see: https://github.com/systemd/systemd/issues/7986#issuecomment-371315606

So I still need to fix this case.
Comment 7 Johannes Segitz 2018-05-02 12:20:51 UTC
(In reply to Franck Bui from comment #6)
https://github.com/systemd/systemd/pull/8822 seems to be moving in the right direction. Thank you for your work
Comment 8 Johannes Segitz 2018-06-22 12:05:03 UTC
(In reply to Johannes Segitz from comment #7)
so with the MASTER having reviewed you pull request this should be able to merge. Can you please also provide a maintenance request based on this? Thank you
Comment 9 Franck Bui 2018-06-25 08:41:09 UTC
Nope it's still not yet finished, there're still some issues to address and given that it's low priority, I don't have time to rework the patchset yet.

Also since it's a considerable rewrite, I'm still wondering if it will be a good idea to backport since it will require a lotof work *and* will have a non negligible risk of regression. Given that this security issue seems to have low impact, I'm wondering...
Comment 20 Ruediger Oertel 2018-11-27 16:11:28 UTC
I think systemd is absolutely correct refusing user-owned / in this case.

But I also think the user should not have to search for days why his system
does not work as expected. Printing the reason for the refused transition
would make a lot of sense, IMHO.
Comment 21 Marcus Meissner 2018-11-28 12:37:14 UTC
So is the idea to replace tmpfiles with the upstream version instead of massive patchsets backporting?

Has the syntax changed?

Otherwise it sounds sensible.
Comment 22 Franck Bui 2018-11-28 12:57:45 UTC
(In reply to Marcus Meissner from comment #21)
> So is the idea to replace tmpfiles with the upstream version instead of
> massive patchsets backporting?
> 

As  said in comment #18, the result of the patchset is pretty similar to the upstream version. But in this case the fix has been imported step by step and that should be safer than importing directly the upstream version.

> Has the syntax changed?

Nope and the patchset shouldn't include any new features unlike the import of the upstream version.

Actually the pending question is more: does it worth fixing CVE-2018-6954 and should we take the risk to import such massive changes ?

If so I think we should use the patchset, at least for SLE15.
Comment 23 Benjamin Brunner 2018-12-10 13:14:44 UTC
Security, do you think the risk is worth it and would it be acceptable to use the patchset for SLE-15?
This is also used in Factory and we haven't received any reports about regressions yet. But I think this needs to be tested very carefully by QAM.

Additionally, would it be ok to do SLE-12 afterwards, if nothing bad occurred with SLE-15, given the complexity of the patches?

Thanks in advance.
Comment 24 Johannes Segitz 2018-12-12 13:13:27 UTC
(In reply to Benjamin Brunner from comment #23)
I think we should do it. Doing it first on SLE 15 also sounds like a good idea to spread out the risk. 

I wish there would be a better way, but I don't think we can leave it unfixed and so far this seems to be the only other option
Comment 26 Marcus Meissner 2018-12-22 13:44:43 UTC
via oss-security

Product: systemd (tmpfiles)
Versions-affected: 239 and earlier
Author: Michael Orlitzky
Fixed-in: v240
Bug-report: https://github.com/systemd/systemd/issues/7986
Acknowledgments:
   Franck Bui of SUSE put forth a massive amount of effort to fix this,
   and Lennart Poettering consistently provided timely reviews over the
   course of a few months.


== Summary ==

Before version 240, the systemd-tmpfiles program will follow symlinks 
present in a non-terminal path component while adjusting permissions and 
ownership. Often -- and particularly with "Z" type entries -- an 
attacker can introduce such a symlink and take control of arbitrary 
files on the system to gain root. The "fs.protected_symlinks" sysctl 
does not prevent this attack. Version 239 contained a partial fix, but 
only for the easy-to-exploit recursive "Z" type entries.


== Details ==

The systemd-tmpfiles program tries to avoid following symlinks in the 
last component of a path. To that end, the following trick is used in 
src/tmpfiles/tmpfiles.c:

   fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH);
   ...
   xsprintf(fn, "/proc/self/fd/%i", fd);
   ...
   if (chown(fn, ...

The call to chown will follow the "/proc/self/fd/%i" symlink, but only 
once; it will then operate on the real file described by fd.

However, there is another way to exploit the code above. The call to 
open() will follow symlinks if they appear in a non-terminal component 
of path, even with the O_NOFOLLOW flag set. Citing the open(2) man page,

   O_NOFOLLOW

   If pathname is a symbolic link, then the open fails, with the error
   ELOOP. Symbolic links in earlier components of the pathname will still
   be followed.

So, for example, if the path variable contains "/run/foo/a/b" and if "a" 
is a symlink, then open() will follow it. If systemd-tmpfiles will be 
changing ownership of "/run/foo/a/b" after that of "/run/foo", then the 
owner of "/run/foo" can exploit that fact to gain root by replacing 
"/run/foo/a" with a symlink. With a Z-type tmpfiles.d entry, the 
attacker can create this situation himself.

The "fs.protected_symlinks" sysctl does not protect against these sorts 
of attacks. Due to the widespread and legitimate use of symlinks in 
situations like these, the symlink protection is much weaker than the 
corresponding hardlink protection.


== Exploitation ==

Consider the following entry in /etc/tmpfiles.d/exploit-recursive.conf:

   d /var/lib/systemd-exploit-recursive 0755 mjo mjo
   Z /var/lib/systemd-exploit-recursive 0755 mjo mjo

Once systemd-tmpfiles has been started once, my "mjo" user will own that 
directory:

   mjo $ sudo ./build/systemd-tmpfiles --create
   mjo $ ls -ld /var/lib/systemd-exploit-recursive
   drwxr-xr-x  2 mjo mjo 4.0K 2018-02-13 09:38 /var/lib/systemd...

At this point, I am able to create a directory "foo" and a file 
"foo/passwd" under /var/lib/systemd-exploit-recursive. The next time 
that systemd-tmpfiles is run (perhaps after a reboot), the tmpfiles.c 
function item_do_children() will be called on "foo". Within that 
function, there is a macro FOREACH_DIRENT_ALL that loops through the 
entries of "foo".

The FOREACH_DIRENT_ALL macro defers to readdir(3), and thus requires the 
real directory stream pointer for "foo", because we want it to see 
"foo/passwd". However, while the macro is iterating, the "q = action(i, 
p)" will be performed on "p" which consists of the path "foo" and some 
filename "d", but without reference to its file descriptor. So, between 
the time that item_do_children() is called on "foo" and the time that "q 
= action(i, p)" is run on "foo/passwd", I have the opportunity to 
replace "foo" with a symlink to "/etc", causing "/etc/passwd" to be 
affected by the change of ownership and permissions.

But there's more: the FOREACH_DIRENT_ALL macro processes the contents of 
"foo" in whatever order readdir() returns them. Since mjo owns "foo", I 
can fill it with junk to buy myself as much time as I like before 
"foo/passwd" is reached:

   mjo $ cd /var/lib/systemd-exploit-recursive
   mjo $ mkdir foo
   mjo $ cd foo
   mjo $ echo $(seq 1 500000) | xargs touch
   mjo $ touch passwd

Now, restarting systemd-tmpfiles will change ownership of all of those 
files...

   mjo $ sudo ./build/systemd-tmpfiles --create

and it takes some time for it to process the 500,000 dummy files before 
reaching "foo/passwd". At my leisure, I can replace foo with a symlink:

   mjo $ cd /var/lib/systemd-exploit-recursive
   mjo $ mv foo bar && ln -s /etc ./foo

After some time, systemd-tmpfiles will eventually reach the path 
"foo/passwd", which now points to "/etc/passwd", and grant me root access.

A similar, but more difficult attack works against non-recursive entry 
types. Consider the following tmpfiles.d entry:

   d /var/lib/systemd-exploit 0755 mjo mjo
   d /var/lib/systemd-exploit/foo 0755 mjo mjo
   f /var/lib/systemd-exploit/foo/bar 0755 mjo mjo

After "/var/lib/systemd-exploit/foo" is created but before the 
permissions are adjusted on "/var/lib/systemd-exploit/foo/bar", there is 
a short window of opportunity for me to replace "foo" with a symlink to 
(for example) "/etc/env.d". If I'm fast enough, tmpfiles will open 
"foo/bar", following the "foo" symlink, and give me ownership of 
something sensitive in the "/etc/env.d" directory. However, this attack 
is more difficult because I can't arbitrary widen my own window of 
opportunity with junk files, as was possible with the "Z" type entries.


== Resolution ==

Commit 936f6bdb, which is present in systemd v239, changes the recursive 
loop in two important ways. First, it passes file descriptors -- rather 
than parent paths -- to each successive iteration. That allows the next 
iteration to use the openat() system call, eliminating the non-terminal 
path components from the equation. Second, it ensures that each "open" 
call has the O_NOFOLLOW and O_PATH flags to prevent symlinks from being 
followed at the current depth. Note: only the recursive loop was made 
safe; the call to open() the top-level path will still follow 
non-terminal symlinks and is vulnerable to the second attack above.

The commits in pull request 8822 aim to make everything safe from this 
type of symlink attack. As far as tmpfiles is concerned, the main idea 
is to use the chase_symlinks() function in place of the open() system 
call. Since chase_symlinks() calls openat() recursively from the root 
up, it will never follow a non-terminal symlink. Commit 1f56e4ce then 
introduces the CHASE_NOFOLLOW flag for that function, preventing it from 
following terminal symlinks. In subsequent commits (e.g. addc3e30), the 
consumers of chase_symlinks() were updated to pass CHASE_NOFOLLOW to 
chase_symlinks(), preventing them from following any symlinks.

The complete fix is available in systemd v240.
Comment 29 Swamp Workflow Management 2019-01-21 20:09:44 UTC
SUSE-SU-2019:0137-1: An update that solves four vulnerabilities and has 7 fixes is now available.

Category: security (important)
Bug References: 1005023,1045723,1076696,1080919,1093753,1101591,1111498,1114933,1117063,1119971,1120323
CVE References: CVE-2018-16864,CVE-2018-16865,CVE-2018-16866,CVE-2018-6954
Sources used:
SUSE Linux Enterprise Module for Open Buildservice Development Tools 15 (src):    systemd-234-24.20.1, systemd-mini-234-24.20.1
SUSE Linux Enterprise Module for Basesystem 15 (src):    systemd-234-24.20.1
Comment 30 Swamp Workflow Management 2019-01-29 14:16:47 UTC
openSUSE-SU-2019:0098-1: An update that solves four vulnerabilities and has 7 fixes is now available.

Category: security (important)
Bug References: 1005023,1045723,1076696,1080919,1093753,1101591,1111498,1114933,1117063,1119971,1120323
CVE References: CVE-2018-16864,CVE-2018-16865,CVE-2018-16866,CVE-2018-6954
Sources used:
openSUSE Leap 15.0 (src):    systemd-234-lp150.20.12.1, systemd-mini-234-lp150.20.12.1
Comment 31 Craig Hai 2019-03-14 17:51:13 UTC
Regarding the fix for SLE12, it's been left unfixed for some time. Any update?
Comment 42 Franck Bui 2019-04-30 06:06:20 UTC
The fix has been finally submitted to SLE12-SP2+ \o/

Re-assigning to the security team.
Comment 44 Craig Hai 2019-04-30 15:31:38 UTC
Thanks Frank \o/
Comment 46 Swamp Workflow Management 2019-05-16 13:37:52 UTC
SUSE-SU-2019:1265-1: An update that solves three vulnerabilities and has 8 fixes is now available.

Category: security (important)
Bug References: 1080919,1121563,1125352,1126056,1127557,1128657,1130230,1132348,1132400,1132721,955942
CVE References: CVE-2018-6954,CVE-2019-3842,CVE-2019-6454
Sources used:
SUSE OpenStack Cloud 7 (src):    systemd-228-150.66.4
SUSE Linux Enterprise Software Development Kit 12-SP4 (src):    systemd-228-150.66.4
SUSE Linux Enterprise Software Development Kit 12-SP3 (src):    systemd-228-150.66.4
SUSE Linux Enterprise Server for SAP 12-SP2 (src):    systemd-228-150.66.4
SUSE Linux Enterprise Server 12-SP4 (src):    systemd-228-150.66.4
SUSE Linux Enterprise Server 12-SP3 (src):    systemd-228-150.66.4
SUSE Linux Enterprise Server 12-SP2-LTSS (src):    systemd-228-150.66.4
SUSE Linux Enterprise Server 12-SP2-BCL (src):    systemd-228-150.66.4
SUSE Linux Enterprise Desktop 12-SP4 (src):    systemd-228-150.66.4
SUSE Linux Enterprise Desktop 12-SP3 (src):    systemd-228-150.66.4
SUSE Enterprise Storage 4 (src):    systemd-228-150.66.4
SUSE CaaS Platform ALL (src):    systemd-228-150.66.4
SUSE CaaS Platform 3.0 (src):    systemd-228-150.66.4
OpenStack Cloud Magnum Orchestration 7 (src):    systemd-228-150.66.4

NOTE: This line indicates an update has been released for the listed product(s). At times this might be only a partial fix. If you have questions please reach out to maintenance coordination.
Comment 47 Swamp Workflow Management 2019-05-27 22:10:30 UTC
openSUSE-SU-2019:1450-1: An update that solves three vulnerabilities and has 8 fixes is now available.

Category: security (important)
Bug References: 1080919,1121563,1125352,1126056,1127557,1128657,1130230,1132348,1132400,1132721,955942
CVE References: CVE-2018-6954,CVE-2019-3842,CVE-2019-6454
Sources used:
openSUSE Leap 42.3 (src):    systemd-228-71.1, systemd-mini-228-71.1
Comment 48 Wolfgang Frisch 2020-04-29 09:09:05 UTC
Released.