The latest two ebuilds for mariadb (10.2.7-r2 and 10.2.8) call "chown" on the live root filesystem in pkg_postinst, in more than one place. For example, pkg_postinst() { # Make sure the vars are correctly initialized mysql_init_vars # Check FEATURES="collision-protect" before removing this [[ -d "${ROOT}${MY_LOGDIR}" ]] || \ install -d -m0750 -o mysql -g mysql "${ROOT}${MY_LOGDIR}" # Secure the logfiles touch "${ROOT}${MY_LOGDIR}"/mysql.{log,err} chown mysql:mysql "${ROOT}${MY_LOGDIR}"/mysql* ... This is exploitable by the "mysql" user to gain root. The non-recursive calls to "chown" are vulnerable to having their targets replaced by symlinks, and the recursive calls are vulnerable to the same trick with hard links. For example, 1. emerge mariadb-10.2.8 2. sudo su -s /bin/sh -c 'ln -sf /etc/passwd /var/log/mysql/mysql.log' mysql 3. emerge mariadb-10.2.8 4. The "mysql" user now owns /etc/passwd. I haven't verified the exploit with "chown -R" because I don't want to spend another half hour on the build, but anywhere you call "chown -R" on the same directory more than once is going to be exploitable.
This is more than mariadb. This code has existed for years and years in mysql.eclass (Overlay only now), mysql-v2.eclass, mysql-multilib*.eclass and now finally in the dev-db/mariadb ebuilds. All versions of dev-db/{mysql,mariadb,percona-server,mysql-cluster,mariadb-galera} are included. As well as past versions of dev-db/mysql-community. I'm testing a patch for existence of non files so they can be removed at the log locations. Any further suggestions are welcome.
(In reply to Brian Evans from comment #1) > > Any further suggestions are welcome. If the code has been around for that long, consider the possibility that your predecessors were wacko =) The only person who can change ownership of a file is root, so if any of the log files have the wrong ownership, it's because root went in there and did it. So long as /var/log/mysql is writable by the "mysql" user, the logs will get created as mysql:mysql and everything will be fine. Symlinks are easy to detect, but hard links are a lot, uh, harder -- and they have the same problem.
(In reply to Michael Orlitzky from comment #2) > (In reply to Brian Evans from comment #1) > > > > Any further suggestions are welcome. > > If the code has been around for that long, consider the possibility that > your predecessors were wacko =) > > The only person who can change ownership of a file is root, so if any of the > log files have the wrong ownership, it's because root went in there and did > it. So long as /var/log/mysql is writable by the "mysql" user, the logs will > get created as mysql:mysql and everything will be fine. > > Symlinks are easy to detect, but hard links are a lot, uh, harder -- and > they have the same problem. OK. I have removed the "# Secure the logfiles" block an just kept the install above it. This does not affect the server since it creates mysqld.err and other files without our help. Let me know when this is OK to push.
(In reply to Brian Evans from comment #3) > > OK. I have removed the "# Secure the logfiles" block an just kept the > install above it. This does not affect the server since it creates > mysqld.err and other files without our help. > I suspect this one is vulnerable too (the others should be OK because they operate on $D and not $ROOT): chown -R mysql:mysql "${ROOT}/${MY_DATADIR}" 2>/dev/null I think the same reasoning applies -- everything in there should have the correct ownership already -- but I'm no expert. If you feel like waiting for me to re-emerge mariadb one more time, I'll see if I can exploit that line above.
(In reply to Michael Orlitzky from comment #4) > (In reply to Brian Evans from comment #3) > > > > OK. I have removed the "# Secure the logfiles" block an just kept the > > install above it. This does not affect the server since it creates > > mysqld.err and other files without our help. > > > > I suspect this one is vulnerable too (the others should be OK because they > operate on $D and not $ROOT): > > chown -R mysql:mysql "${ROOT}/${MY_DATADIR}" 2>/dev/null > > I think the same reasoning applies -- everything in there should have the > correct ownership already -- but I'm no expert. > > If you feel like waiting for me to re-emerge mariadb one more time, I'll see > if I can exploit that line above. While possible, this is unlikely as an attacker would have to expect that an admin is going to install a database and run 'emerge --config' and therefore create the directory in ${MY_DATADIR} which defaults to /var/lib/mysql but can be overridden in the environment by the admin at build time.
Hmm.. or otherwise remove the database directory entirely, which an admin will certainly notice, place an exploit and expect 'emerge --config' again. A good admin should spot such trouble and prepare a new directory, but I understand this is not always the case.
Well, pkg_config won't run if /var/lib/mysql already exists, so that eliminates the easy exploit where I just stick a hard link in there whenever I feel like it and later run emerge --config. There's still a race condition I think, but that's much less serious: 1. local cmd=( "${EROOT}usr/share/mariadb/scripts/mysql_install_db" ) 2. [[ -f "${cmd}" ]] || cmd=( "${EROOT}usr/bin/mysql_install_db" ) ... 5. "${cmd[@]}" >"${TMPDIR}"/mysql_install_db.log 2>&1 ... 12. chown -R mysql:mysql "${ROOT}/${MY_DATADIR}" 2>/dev/null I've probably miscounted those, but between lines 5 and 12, the "mysql" user can stick a hard link in MY_DATADIR. He could maybe clue himself into that by watching $TMPDIR for the creation of a file named mysql_install_db.log, but this is all pretty far-fetched. Is the "chown -R" doing anything at all? If under normal circumstances, everything in that directory has the correct owner anyway, then I would say it's worthwhile to remove it just in case.
Aw shit... so, the race condition is actually stupid easy to take advantage of. Run this in a shell as the "mysql" user: while true ; do ln /etc/passwd /var/lib/mysql/x done As soon as you run emerge --config, even with a brand new MY_DATADIR, the "mysql" user will take ownership of /etc/passwd.
(In reply to Michael Orlitzky from comment #7) > Well, pkg_config won't run if /var/lib/mysql already exists, so that > eliminates the easy exploit where I just stick a hard link in there whenever > I feel like it and later run emerge --config. > > There's still a race condition I think, but that's much less serious: > > 1. local cmd=( "${EROOT}usr/share/mariadb/scripts/mysql_install_db" ) > 2. [[ -f "${cmd}" ]] || cmd=( "${EROOT}usr/bin/mysql_install_db" ) > ... > 5. "${cmd[@]}" >"${TMPDIR}"/mysql_install_db.log 2>&1 > ... > 12. chown -R mysql:mysql "${ROOT}/${MY_DATADIR}" 2>/dev/null > > I've probably miscounted those, but between lines 5 and 12, the "mysql" user > can stick a hard link in MY_DATADIR. He could maybe clue himself into that > by watching $TMPDIR for the creation of a file named mysql_install_db.log, > but this is all pretty far-fetched. > > Is the "chown -R" doing anything at all? If under normal circumstances, > everything in that directory has the correct owner anyway, then I would say > it's worthwhile to remove it just in case. The mysql_install_db script is run as root and the data files created get a group of root so is this line acceptable to replace line 12 above? chgrp mysql "${ROOT}/${MY_DATADIR}"/{mysql,test} 2>/dev/null
(In reply to Brian Evans from comment #9) > > The mysql_install_db script is run as root and the data files created get a > group of root so is this line acceptable to replace line 12 above? > > chgrp mysql "${ROOT}/${MY_DATADIR}"/{mysql,test} 2>/dev/null What's the owner/group on $MY_DATADIR at that point? The chgrp will also affect the target of sym/hard links, so it's important that the mysql user not be able to stick a link in place of those two arguments via the same race condition. If MY_DATADIR is root:root, it should be safe. Another thing that might work is to "su mysql ..." instead of passing --user to mysql_install_db.
(In reply to Michael Orlitzky from comment #10) > (In reply to Brian Evans from comment #9) > > > > The mysql_install_db script is run as root and the data files created get a > > group of root so is this line acceptable to replace line 12 above? > > > > chgrp mysql "${ROOT}/${MY_DATADIR}"/{mysql,test} 2>/dev/null > > What's the owner/group on $MY_DATADIR at that point? The chgrp will also > affect the target of sym/hard links, so it's important that the mysql user > not be able to stick a link in place of those two arguments via the same > race condition. If MY_DATADIR is root:root, it should be safe. > > Another thing that might work is to "su mysql ..." instead of passing --user > to mysql_install_db. The end result of pkg_config() without the chown -R is that the mysql and test directories have a mysql:root with 0700 permissions. I simply wanted to keep those directories as they should be.
(In reply to Michael Orlitzky from comment #10) > Another thing that might work is to "su mysql ..." instead of passing --user > to mysql_install_db. "su mysql ..." is not an option as the user does not have a shell and the command fails.
(In reply to Brian Evans from comment #12) > > "su mysql ..." is not an option as the user does not have a shell and the > command fails. You should be able to override that with --shell.
Created attachment 494538 [details] bug-630822.patch Proposed patch for 10.2.8 and similar in eclasses. I was not able to use su effectively and break out the array. This still does the same job and only does chgrp on special directories.
Brian fixed this last night: https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=5a4dfd9b7b9e8e8d23315dfd3afb4bbfc56b9de8 https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=40984ffe654ae90069740e76c6406ec5f8d3ed9f https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=b19f6190519f94450e4aa23cde091b8fd24d763d Can we make this public so that I can reference it in the CVE request?
(In reply to Michael Orlitzky from comment #15) > > Can we make this public so that I can reference it in the CVE request? Ping =)
Thank you Michael for the report and thank you Brian for the fix. Like said in comment #15, everything is done. No bumps or user action required. Vulnerability only existed while emerging one of the affected ebuilds/ebuilds using affected eclasses.
(In reply to Thomas Deutschmann from comment #17) > Thank you Michael for the report and thank you Brian for the fix. > > Like said in comment #15, everything is done. No bumps or user action > required. Vulnerability only existed while emerging one of the affected > ebuilds/ebuilds using affected eclasses. That's not *entirely* true.. the pkg_config phase had a similar problem, so if you installed a vulnerable version last month and waited until today to set it up, then you might want to upgrade first. That scenario is a bit outlandish, though, because someone needs to gain access to the "mysql" user in order to do anything. If the database was never configured, that's next to impossible. The only plausible scenario is a pre-existing, exploited mysql installation that has had its databases wiped (and now needs them re-created). I won't be offended if you decide not to spend your time warning people about that.
Re-opened to evaluate which packages need a rev bump. Thanks, Michael.
(In reply to Thomas Deutschmann from comment #19) > Re-opened to evaluate which packages need a rev bump. > > Thanks, Michael. What will portage do if you try to `emerge --config` a version that no longer exists? If it uses a newer version's pkg_config, or if it forces you to upgrade first -- then there's actually no need for a new revision: users need only sync their trees to remove the vulnerable code.
(In reply to Michael Orlitzky from comment #20) > What will portage do if you try to `emerge --config` a version that no > longer exists? If it uses a newer version's pkg_config, or if it forces you > to upgrade first -- then there's actually no need for a new revision: users > need only sync their trees to remove the vulnerable code. When you emerge an ebuild, everything, including used eclasses, will be cached in the package db. This allows the user to run "emerge --config" at anytime to get the expected results even if we have removed the code from the eclass in the meantime. However, if there was a problem in the ebuild/eclass, the user has to re-emerge the ebuild to get the fix. That's the problem in this case. dev-db/mariadb: - Affected by pkg_postinst: Fixed in >=mariadb-10.2.9. Affected mariadb-10.2.x ebuilds are removed. => pkg_postinst vulnerability is fixed! - Affected by eclass: Fixed eclasses available since 2017-09-28 22:30:51 -0400. However, users with - mariadb-10.0.30 - mariadb-10.0.32 - mariadb-10.1.24 - mariadb-10.1.26 installed are probably affected if they have emerged the package before the date mentioned above. dev-db/mysql: - Affected by pkg_postinst: - - Affected by eclass: Fixed eclasses available since 2017-09-28 22:30:51 -0400. However, users with - mysql-5.6.36 - mysql-5.6.37 installed are probably affected if they have emerged the package before the date mentioned above. dev-db/percona-server: - Affected by pkg_postinst: - - Affected by eclass: Fixed eclasses available since 2017-09-28 22:30:51 -0400. However, users with - percona-server-5.6.37.82.2 installed are probably affected if they have emerged the package before the date mentioned above. dev-db/mysql-cluster: - Affected by pkg_postinst: - - Affected by eclass: Fixed eclasses available since 2017-09-28 22:30:51 -0400. However, users with - mysql-cluster-7.2.22 - mysql-cluster-7.3.11 installed are probably affected if they have emerged the package before the date mentioned above. dev-db/mariadb-galera: - Affected by pkg_postinst: - - Affected by eclass: - (only available ebuild in repository was bumped after eclasses were fixed)
(In reply to Thomas Deutschmann from comment #21) > > When you emerge an ebuild, everything, including used eclasses, will be > cached in the package db. This allows the user to run "emerge --config" at > anytime to get the expected results even if we have removed the code from > the eclass in the meantime. > > However, if there was a problem in the ebuild/eclass, the user has to > re-emerge the ebuild to get the fix. Thanks, that's important to know. I see now that portage is saving the ebuilds, but are the eclasses kept somewhere too? The PMS doesn't say what goes on in the VDB, so regardless, I think we should tell everyone to update or reinstall, just to keep things simple. I requested a CVE for this (CVE-2017-15945), and my suggested fix is the following: 1. Update your ::gentoo tree. 2. Upgrade the affected packages to the latest version that your package manager suggests. 3. If an affected package was not upgraded in the previous step (for example, if no new version was available), then reinstall that package manually. That should cover everyone, I think?
(In reply to Michael Orlitzky from comment #22) > I see now that portage is saving the ebuilds, but are the eclasses > kept somewhere too? The used eclasses are cached in "environment.bz2". > 3. If an affected package was not upgraded in the previous step (for > example, if no new version was available), then reinstall that package > manually. I will file vulnerability bugs for each affected package to track the bumping. Everything else is not an option. Think about binary package users.
(In reply to Thomas Deutschmann from comment #23) > > The used eclasses are cached in "environment.bz2". Aha! > I will file vulnerability bugs for each affected package to track the > bumping. Everything else is not an option. Think about binary package users. In hindsight, that is the simplest thing to do, and it makes the advice for end users easier to understand. I can update the CVE page afterwards. Thanks again.
I updated the CVE with the full list of affected packages, and the new mitigation instructions ("just upgrade").
All good here? https://security.gentoo.org/glsa/201711-04