symlink attack vulnerability in init/helper scripts
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
postgresql-common (Ubuntu) |
Fix Released
|
Undecided
|
Unassigned | ||
Precise |
Fix Released
|
Undecided
|
Leonidas S. Barbosa | ||
Trusty |
Fix Released
|
Undecided
|
Marc Deslauriers | ||
Xenial |
Fix Released
|
Undecided
|
Marc Deslauriers | ||
Zesty |
Fix Released
|
Undecided
|
Marc Deslauriers | ||
Artful |
Fix Released
|
Undecided
|
Marc Deslauriers | ||
Bionic |
Fix Released
|
Undecided
|
Unassigned |
Bug Description
Please keep information from this thread confidential until 2017-11-09, and
please reply with the information requested below. As a special exception, I
encourage you to share it with your distribution's own security team.
The PostgreSQL security team has learned of a class of vulnerabilities
affecting major PostgreSQL distributions represented here. The security team
is disclosing this to packagers in advance of the public so each of you can
find and fix vulnerable code in your distributions.
Scripts that the root user calls to start PostgreSQL are often vulnerable to
symlink attack through their log file handling. If the postgres user can
replace the log file with a symbolic link, vulnerable scripts will append to
the link target, as root, at the next postmaster start. (The link can point
anywhere in the filesystem, and the target need not yet exist.) Some scripts
also chown() or chmod() the log file, and one can attack them to redirect
those calls to arbitrary targets. By writing a file like /etc/ld.so.preload,
one can exploit this to run arbitrary code as root. Some distribution scripts
make this simple to exploit, while others leave only a narrow race window.
Scripts PostgreSQL distributes in contrib/
PostgreSQL will use CVE-2017-12172 for the contrib/
vulnerability; if you maintain your own vulnerable scripts, I recommend
acquiring your own CVE.
The problem arises when root alters files located in a directory writable to a
non-root user. (If any parent directory is writable to a non-root user, that
triggers the same problem.) I know of three ways to close the vulnerability:
1. Perform the filesystem operations as the directory owner UID, not as root.
Prefer this method. The contrib/
moves log creation inside the "su postgres" command.
2. Create the file with open(filename, O_EXCL | O_CREAT, mode) only. Do not
pass the filename to any other mutating system call, particularly not
chown() or chmod(). (It's fine to issue fchown() or fchmod().) This has
no known advantage over method (1) and is inconvenient in shell scripts.
Under these conditions, there is no safe, portable way to open for writing
an already-existing file. This method does not suffice if any higher
parent directory is writable to a non-root user.
3. Move the file to a root-owned directory hierarchy. This is good for new
designs, but it may be too disruptive in a security patch.
Note that testing for the presence of symlinks is insufficient, because it
invariably suffers from a TOCTOU race condition.
This vulnerability already saw limited public recognition in
https:/
from that page thwarted the most potent attacks, but I think it was
incomplete. After the sysopen() and before the chown(), an attacker can
replace the chown() target with a symlink to an important root-owned file.
I have attached the contrib/
bolder than I would use in a distribution. It creates postgres-owned log
files instead of root-owned files, and users must manually chown or chmod
their old log files once when installing the fix. That is okay since
installing new contrib/
update would likely choose to handle the ownership change transparently.
Disclosure Timeline:
- 2017-11-06: Starting on this date, I welcome pushing fixes to public source
repositories. The contrib/
git repository. Please continue to refrain from making announcements or
otherwise discussing the fixes in public.
- 2017-11-09: I welcome free discussion of any aspect of this vulnerability,
in any forum. I encourage you to make any announcements on this date.
Please reply in the next week or two with answers to the following:
1. Which PostgreSQL distribution(s) do you handle?
2. Link to the source repository containing your start scripts, if public.
3. Are your scripts vulnerable?
4. If yes, do you plan to fix your scripts the week of 2017-11-09?
I encourage you to discuss implementation of your fix on this thread. When
you have a fix ready, I recommend posting the patch to this thread. This
allows us all to review and look for holes.
If you have questions or information that you prefer not to share with the
entire -packagers community, feel free to start a thread on
<email address hidden> to discuss the matter alone with the security team.
If you credit issue reporters, please credit Antoine Scemama of Brainloop. (I
did that when requesting the contrib/
Thanks,
nm
---
Christoph replied for Debian:
Re: Noah Misch 2017-10-10 <email address hidden>
> The problem arises when root alters files located in a directory writable to a
> non-root user. (If any parent directory is writable to a non-root user, that
> triggers the same problem.) I know of three ways to close the vulnerability:
>
> 1. Perform the filesystem operations as the directory owner UID, not as root.
> 2. Create the file with open(filename, O_EXCL | O_CREAT, mode) only.
> 3. Move the file to a root-owned directory hierarchy.
4. Use lchown()
> This vulnerability already saw limited public recognition in
> https:/
> from that page thwarted the most potent attacks, but I think it was
> incomplete. After the sysopen() and before the chown(), an attacker can
> replace the chown() target with a symlink to an important root-owned file.
Direct link to patch in question:
https:/
Thanks for the analysis there. When patching it for CVE-2016-1255, I
was mostly thinking of clusters owned by random non-postgres users
(postgresql-common can handle clusters owned by arbitrary users) which
would likely have uid >= 500, so chown() wouldn't be invoked, but of
course the core problem is that it presents a postgres->root privilege
escalation.
I think the simple fix there is
use POSIX qw(lchown);
...
lchown $info{'owneruid'}, $g, $info{'logfile'} if (defined $g);
pg_ctlcluster has unfortunately grown over the years to be horrible
spaghetti code that changes between invoking user (likely root) and
the cluster owner back and forth a few times. I've always meant to
refactor it (but there's only 24h a day). Hopefully there's not more
problems of that sort in there.
> Please reply in the next week or two with answers to the following:
> 1. Which PostgreSQL distribution(s) do you handle?
Debian, apt.postgresql.org, indirectly also Ubuntu.
> 2. Link to the source repository containing your start scripts, if public.
The full pg_ctlcluster file is there, if anyone wants to have a look:
https:/
Another source of problems might be pg_createcluster.
> 3. Are your scripts vulnerable?
Yes.
> 4. If yes, do you plan to fix your scripts the week of 2017-11-09?
Yes.
---
Related and on top there is [1], fixed by [2] which we would be open in Trusty + Xenial (but fixed in zesty).
[1]: https:/
[2]: https:/
CVE References
Changed in postgresql-common (Ubuntu Xenial): | |
status: | New → Confirmed |
Changed in postgresql-common (Ubuntu Zesty): | |
status: | New → Confirmed |
Changed in postgresql-common (Ubuntu Artful): | |
status: | New → Confirmed |
Changed in postgresql-common (Ubuntu Bionic): | |
status: | New → Confirmed |
Changed in postgresql-common (Ubuntu Precise): | |
assignee: | nobody → Marc Deslauriers (mdeslaur) |
assignee: | Marc Deslauriers (mdeslaur) → Leonidas S. Barbosa (leosilvab) |
Changed in postgresql-common (Ubuntu Trusty): | |
assignee: | nobody → Marc Deslauriers (mdeslaur) |
Changed in postgresql-common (Ubuntu Xenial): | |
assignee: | nobody → Marc Deslauriers (mdeslaur) |
Changed in postgresql-common (Ubuntu Zesty): | |
assignee: | nobody → Marc Deslauriers (mdeslaur) |
Changed in postgresql-common (Ubuntu Artful): | |
assignee: | nobody → Marc Deslauriers (mdeslaur) |
Changed in postgresql-common (Ubuntu Bionic): | |
status: | Confirmed → Fix Released |
Changed in postgresql-common (Ubuntu Precise): | |
status: | Confirmed → Fix Released |
information type: | Private Security → Public Security |
Planned Debian fix: https:/ /anonscm. debian. org/cgit/ pkg-postgresql/ postgresql- common. git/commit/ ?id=8b4d0a889a8 287181c4bdf4646 2db9b737a6e25d
Christoph