Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 629398 - add QA warning for system executables writable by a non-root user
Summary: add QA warning for system executables writable by a non-root user
Status: RESOLVED FIXED
Alias: None
Product: Portage Development
Classification: Unclassified
Component: Enhancement/Feature Requests (show other bugs)
Hardware: All Linux
: Normal normal (vote)
Assignee: Portage team
URL:
Whiteboard:
Keywords: InVCS
Depends on:
Blocks: 659322
  Show dependency tree
 
Reported: 2017-08-30 20:10 UTC by Michael Orlitzky
Modified: 2018-10-12 19:27 UTC (History)
0 users

See Also:
Package list:
Runtime testing required: ---


Attachments
91bad-bin-owner (91bad-bin-owner,835 bytes, text/plain)
2017-08-30 20:44 UTC, Michael Orlitzky
Details
92bad-bin-group-write (92bad-bin-group-write,915 bytes, text/plain)
2017-08-30 23:20 UTC, Michael Orlitzky
Details
90bad-bin-owner (90bad-bin-owner,1.06 KB, text/plain)
2017-08-31 14:36 UTC, Michael Orlitzky
Details
90bad-bin-group-write (90bad-bin-group-write,1.20 KB, text/plain)
2017-08-31 14:38 UTC, Michael Orlitzky
Details
90bad-bin-group-write (90bad-bin-group-write,1.22 KB, text/plain)
2017-09-06 20:15 UTC, Michael Orlitzky
Details
90bad-bin-owner (90bad-bin-owner,1.08 KB, text/plain)
2017-09-06 20:17 UTC, Michael Orlitzky
Details
90bad-bin-owner (90bad-bin-owner,1.22 KB, text/plain)
2018-07-25 12:43 UTC, Michael Orlitzky
Details
90bad-bin-group-write (90bad-bin-group-write,1.37 KB, text/plain)
2018-07-25 12:55 UTC, Michael Orlitzky
Details
0001-bin-install-qa-check.d-add-new-90bad-bin-owner-QA-ch.patch (0001-bin-install-qa-check.d-add-new-90bad-bin-owner-QA-ch.patch,2.49 KB, patch)
2018-07-29 17:24 UTC, Michael Orlitzky
Details | Diff
0002-bin-install-qa-check.d-add-new-90bad-bin-group-write.patch (0002-bin-install-qa-check.d-add-new-90bad-bin-group-write.patch,2.86 KB, patch)
2018-07-29 17:24 UTC, Michael Orlitzky
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Michael Orlitzky gentoo-dev 2017-08-30 20:10:25 UTC
In bug 629380 we have a pretty silly security hole: a daemon in /usr/sbin is owned by a non-root user, so he can replace it to gain root the next time the daemon is started.

I'd like to add a QA check for this... there's already a 90world-writable check, so we don't have to worry about that case. What's left?

  * Files in /sbin or /usr/sbin that are owned by a non-root user.

  * Symlinks in /sbin or /usr/sbin whose target is owned by a non-root user.

  * Group-writable files in /sbin or /usr/sbin with a non-root group.

Did I miss anything that's safe to warn about?

Will these generate false positives on prefix, and if so, how can I skip them there?
Comment 1 Mike Gilbert gentoo-dev 2017-08-30 20:24:10 UTC
Why is sbin special? It seems like it would be just as dangerous for a random user to own files in /bin or /usr/bin.
Comment 2 Michael Orlitzky gentoo-dev 2017-08-30 20:29:24 UTC
(In reply to Mike Gilbert from comment #1)
> Why is sbin special? 

Now that I think about it, I guess it isn't.
Comment 3 Michael Orlitzky gentoo-dev 2017-08-30 20:44:11 UTC
Created attachment 491116 [details]
91bad-bin-owner

Proof of concept, this catches the /usr/sbin/nagios problem but I've no idea what it does on prefix.

Still need the group-writable check too.
Comment 4 Mike Gilbert gentoo-dev 2017-08-30 21:08:50 UTC
I think this could be made more generic still.

Is it valid for a non-root user to own *any* file installed by a package?
Comment 5 Michael Orlitzky gentoo-dev 2017-08-30 21:26:17 UTC
(In reply to Mike Gilbert from comment #4)
> I think this could be made more generic still.
> 
> Is it valid for a non-root user to own *any* file installed by a package?

In a perfect world, probably not, but check e.g. /var/log.
Comment 6 Michael Orlitzky gentoo-dev 2017-08-30 23:20:22 UTC
Created attachment 491120 [details]
92bad-bin-group-write

Same caveats apply to this one.

Miscellany:

  * "local" isn't POSIX
  * it would be faster to pipe "find" through sed
  * the output should probably be formatted as a list, like the "pre-stripped"
    warning
  * spaces instead of tabs........
Comment 7 Fabian Groffen gentoo-dev 2017-08-31 06:28:15 UTC
(In reply to Michael Orlitzky from comment #6)
> Created attachment 491120 [details]
> 92bad-bin-group-write
> 
> Same caveats apply to this one.
> 
> Miscellany:
> 
>   * "local" isn't POSIX

Portage extensively uses bash features, and depends on it, so this shouldn't be a problem?

>   * it would be faster to pipe "find" through sed
>   * the output should probably be formatted as a list, like the
> "pre-stripped"
>     warning
>   * spaces instead of tabs........

If you'd replace D with ED in your for-loop you would add Prefix support.  Leave D in the strip section.
Comment 8 Michael Orlitzky gentoo-dev 2017-08-31 11:25:09 UTC
(In reply to Mike Gilbert from comment #4)
> I think this could be made more generic still.
> 
> Is it valid for a non-root user to own *any* file installed by a package?

A grep of the tree for "fowners" actually turns up quite a few root exploits, where e.g. an unprivileged user owns the config file for a daemon that starts as root.

On the other hand, it also turns up a ton of "because I could" calls to fowners, that I can't really explain but which don't seem to hurt anything.

Eventually it might make sense to be more strict here, but I think if we turn on the warnings for every installed file right now, the important ones will be drowned out in the noise.


(In reply to Fabian Groffen from comment #7)
> 
> Portage extensively uses bash features, and depends on it, so this shouldn't
> be a problem?
> 

So long as these are supposed to be bash, it's not. I wasn't sure.


> If you'd replace D with ED in your for-loop you would add Prefix support. 
> Leave D in the strip section.

But will throw a QA warning for every executable on prefix because it's owned by e.g. "mjo" and not root?
Comment 9 Fabian Groffen gentoo-dev 2017-08-31 11:30:33 UTC
Ah, I missed the -group root, I'm not sure if mainline portage has rootgid stored somewhere like prefix branch does.
Comment 10 Fabian Groffen gentoo-dev 2017-08-31 11:37:35 UTC
now I think of it, this breaks FreeBSD too, since root's group is wheel (its id = 0).
Comment 11 Michael Orlitzky gentoo-dev 2017-08-31 11:52:19 UTC
(In reply to Fabian Groffen from comment #10)
> now I think of it, this breaks FreeBSD too, since root's group is wheel (its
> id = 0).

Ah, good catch. I'll change it to use -uid instead of -user and likewise for the group.

Does using ${D} instead of ${ED} prevent the false positives on prefix?

And is there a better way to get the "superuser" uid/gid? I considered pulling the uid and gid off of ${D} itself.
Comment 12 Fabian Groffen gentoo-dev 2017-08-31 12:13:48 UTC
Perhaps Zac knows how to get the root/owner uid and gid.  Otherwise, in Prefix this check makes little to no sense, unless run as root.  So if UID=0 you may want to run this check, else skip it.

The ED in your for-loop makes the paths correct, the sed replacement with D is fine, since that way the messages include EPREFIX, which for clarity is good.
Comment 13 Michael Orlitzky gentoo-dev 2017-08-31 14:36:52 UTC
Created attachment 491158 [details]
90bad-bin-owner
Comment 14 Michael Orlitzky gentoo-dev 2017-08-31 14:38:27 UTC
Created attachment 491160 [details]
90bad-bin-group-write

These should clean things up a bit. I copied the [[ $D == $ED ]] test from another check to bail out on prefix installations.
Comment 15 Fabian Groffen gentoo-dev 2017-09-01 08:43:14 UTC
just for the record, there are prefix installs which are using the root user
Comment 16 Michael Orlitzky gentoo-dev 2017-09-04 14:26:17 UTC
(In reply to Fabian Groffen from comment #15)
> just for the record, there are prefix installs which are using the root user

Ok, I see: we can still get some of the benefit on prefix by checking $UID == 0 rather than $D == $ED. That way the check runs on root prefix installs, at least.

What's a safe way to get the current $UID? Can the bash $UID variable be relied-upon in these QA scripts?
Comment 17 Mike Gilbert gentoo-dev 2017-09-04 16:05:44 UTC
(In reply to Michael Orlitzky from comment #16)
> What's a safe way to get the current $UID? Can the bash $UID variable be
> relied-upon in these QA scripts?

Yes, though I would use EUID (effective UID) instead.
Comment 18 Michael Orlitzky gentoo-dev 2017-09-06 20:15:21 UTC
Created attachment 492914 [details]
90bad-bin-group-write
Comment 19 Michael Orlitzky gentoo-dev 2017-09-06 20:17:27 UTC
Created attachment 492916 [details]
90bad-bin-owner

These last two revisions use the EUID == 0 check, and I've updated the output to match the test (mention uid/gid instead of "root").
Comment 20 Michael Orlitzky gentoo-dev 2017-10-16 12:01:11 UTC
What's the current fashion -- should I create a patch for portage, or for metadata/install-qa-check.d in ::gentoo?
Comment 21 Michael Orlitzky gentoo-dev 2018-07-25 00:53:55 UTC
Ping, this is ready to go.
Comment 22 Zac Medico gentoo-dev 2018-07-25 06:59:30 UTC
(In reply to Michael Orlitzky from comment #18)
> Created attachment 492914 [details]
> 90bad-bin-group-write
>
> 	# This check doesn't work on non-root prefix installations at
> 	# the moment, because every executable therein is owned by a
> 	# nonzero GID.
> 	[[ "${EUID}" -eq "0" ]] || return

I'd prefer not to make an assumption about EUID, so how about this instead:

[[ ${PORTAGE_INST_UID} -eq 0 ]]


> 	local d f found=()
> 
> 	for d in "${ED}/bin" "${ED}/usr/bin" "${ED}/sbin" "${ED}/usr/sbin"; do
> 		test -d "${d}" || continue

Let's normalize all ${ED} and ${D} references for the absence of a trailing slash in EAPI 7, like  ${ED%/}/bin.


> 		# Read the results of the "find" command into the "found" bash
> 		# array. Use -L to catch symlinks whose targets are vulnerable.
> 		# We match the GID and not the name "root" here because (for
> 		# example) on FreeBSD, the superuser group is "wheel".
> 		while read -r -d '' f; do
> 			found+=( "${f}" )
> 		done < <(find -L "${d}" -maxdepth 1 -type f -perm /g+w ! -gid 0 -print0)

Let's use -gid ${PORTAGE_INST_GID}.


> 
> 		if [[ ${found[@]} ]]; then
> 			eqawarn "system executables group-writable by nonzero gid:"
> 			for f in "${found[@]}"; do
> 				# Strip off the leading ${D} before outputting the path,
> 				# but leave the prefix if there is one.
> 				eqawarn "  ${f#${D}}"

${D%/}/

> 			done
> 		fi
> 	done
> }
> 
> bad_bin_group_write_check
> :

(In reply to Michael Orlitzky from comment #19)
> Created attachment 492916 [details]
> 90bad-bin-owner
>
> 		while read -r -d '' f; do
> 			found+=( "${f}" )
> 		done < <(find -L "${d}" -maxdepth 1 -type f ! -uid 0 -print0)

Let's use -uid ${PORTAGE_INST_UID}.
Comment 23 Michael Orlitzky gentoo-dev 2018-07-25 12:43:12 UTC
Created attachment 540992 [details]
90bad-bin-owner
Comment 24 Michael Orlitzky gentoo-dev 2018-07-25 12:55:34 UTC
Created attachment 540994 [details]
90bad-bin-group-write

Changes:

  * Update copyright year to 2018.
  * Normalize trailing slashes in D and ED.
  * Mention that "find -L ..." won't catch absolute symlinks on a first install.

New problems:

  * The output is now missing the trailing slash, e.g. "usr/bin/asd" is listed
    instead of "/usr/bin/asd". This isn't a big deal, and could probably be
    fixed by stripping the whole prefix and then putting it back, normalized.

Questions / comments:

The choice to skip these checks when EUID != 0 was made for two reasons. First, we can't use something like PORTAGE_INST_UID if we want these to go in metadata/install-qa-check.d (see my comment #20). But more importantly, we can get false positives if we check for PORTAGE_INST_UID instead of uid=0. For example, on a system where PORTAGE_INST_UID=100, we don't want to spit out warnings for a symlink pointing to a root:root binary.

So my feeling is that these checks should "do no harm" and get skipped on weird systems -- we only need bug reports from *someone*, not *everyone*. From that perspective, bailing out when EUID != 0 is OK.
Comment 25 Zac Medico gentoo-dev 2018-07-26 07:57:31 UTC
(In reply to Michael Orlitzky from comment #24)
> Questions / comments:
> 
> The choice to skip these checks when EUID != 0 was made for two reasons.
> First, we can't use something like PORTAGE_INST_UID if we want these to go
> in metadata/install-qa-check.d (see my comment #20). But more importantly,
> we can get false positives if we check for PORTAGE_INST_UID instead of
> uid=0. For example, on a system where PORTAGE_INST_UID=100, we don't want to
> spit out warnings for a symlink pointing to a root:root binary.

Now I'm wondering what the symlink check is good for, is it going to report files owned by *other* packages? Wouldn't that be a false positive for the package being checked?

> So my feeling is that these checks should "do no harm" and get skipped on
> weird systems -- we only need bug reports from *someone*, not *everyone*.
> From that perspective, bailing out when EUID != 0 is OK.

I'm more concerned about the possibility of it running when it's not supposed to, and I want the code to clearly indicate when it's not supposed to run, so how about this:

[[ ${EUID} -eq 0 || ${PORTAGE_INST_UID} -eq 0 ]] || return
Comment 26 Zac Medico gentoo-dev 2018-07-26 08:01:08 UTC
(In reply to Zac Medico from comment #25)
> I'm more concerned about the possibility of it running when it's not
> supposed to, and I want the code to clearly indicate when it's not supposed
> to run, so how about this:
> 
> [[ ${EUID} -eq 0 || ${PORTAGE_INST_UID} -eq 0 ]] || return

I mean like this:

[[ ${EUID} -ne 0 || ${PORTAGE_INST_UID} -ne 0 ]] && return
Comment 27 Zac Medico gentoo-dev 2018-07-26 08:10:29 UTC
(In reply to Zac Medico from comment #25)
> Now I'm wondering what the symlink check is good for, is it going to report
> files owned by *other* packages? Wouldn't that be a false positive for the
> package being checked?

I see, the current package would be at fault for including the vulnerable file in PATH via the symlink.
Comment 28 Michael Orlitzky gentoo-dev 2018-07-27 18:35:32 UTC
(In reply to Zac Medico from comment #26)
> 
> I mean like this:
> 
> [[ ${EUID} -ne 0 || ${PORTAGE_INST_UID} -ne 0 ]] && return

I'm fine with this change... does that mean I should be preparing patches for portage.git and not metadata/install-qa-check.d, though?


(In reply to Zac Medico from comment #27)
> 
> I see, the current package would be at fault for including the vulnerable
> file in PATH via the symlink.

Right... the check is generally useful and catches real mistakes (for example,   dev-db/aerospike-server-community).
Comment 29 Zac Medico gentoo-dev 2018-07-27 18:37:24 UTC
(In reply to Michael Orlitzky from comment #28)
> (In reply to Zac Medico from comment #26)
> > 
> > I mean like this:
> > 
> > [[ ${EUID} -ne 0 || ${PORTAGE_INST_UID} -ne 0 ]] && return
> 
> I'm fine with this change... does that mean I should be preparing patches
> for portage.git and not metadata/install-qa-check.d, though?

Yes, that would be very helpful.

> (In reply to Zac Medico from comment #27)
> > 
> > I see, the current package would be at fault for including the vulnerable
> > file in PATH via the symlink.
> 
> Right... the check is generally useful and catches real mistakes (for
> example,   dev-db/aerospike-server-community).

Great!
Comment 30 Michael Orlitzky gentoo-dev 2018-07-29 17:24:16 UTC
Created attachment 541712 [details, diff]
0001-bin-install-qa-check.d-add-new-90bad-bin-owner-QA-ch.patch
Comment 31 Michael Orlitzky gentoo-dev 2018-07-29 17:24:51 UTC
Created attachment 541714 [details, diff]
0002-bin-install-qa-check.d-add-new-90bad-bin-group-write.patch
Comment 32 Larry the Git Cow gentoo-dev 2018-08-07 18:49:02 UTC
The bug has been closed via the following commit(s):

https://gitweb.gentoo.org/proj/portage.git/commit/?id=807ac3d9d6eecead73f59d399b30559e5c731587

commit 807ac3d9d6eecead73f59d399b30559e5c731587
Author:     Michael Orlitzky <mjo@gentoo.org>
AuthorDate: 2018-08-07 16:46:04 +0000
Commit:     Zac Medico <zmedico@gentoo.org>
CommitDate: 2018-08-07 18:39:26 +0000

    bin/install-qa-check.d: add new 90bad-bin-group-write QA check.
    
    System executables that are writable by a non-root user pose a
    security risk. Anyone who can write to an executable can change its
    behavior. If that executable is later run with elevated privileges
    (say, by root, when the machine starts), then the non-root user can
    escalate his own privileges to those of the person running the
    modified executable.
    
    The 90bad-bin-owner check already addresses one cause for a non-root
    user to be able to modify an executable: because he owns it. This
    commit adds another check, to ensure that no non-root *groups* have
    write access to any system executables. On a "normal" system, all
    system executables should be writable only by the super-user's group,
    if any. To avoid false-positives, non-"normal" systems (like prefix)
    are skipped.
    
    Closes: https://bugs.gentoo.org/629398

 bin/install-qa-check.d/90bad-bin-group-write | 55 ++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

Additionally, it has been referenced in the following commit(s):

https://gitweb.gentoo.org/proj/portage.git/commit/?id=04e71a831bc42f2a0de1694dd2013eac0414e007

commit 04e71a831bc42f2a0de1694dd2013eac0414e007
Author:     Michael Orlitzky <mjo@gentoo.org>
AuthorDate: 2018-08-07 16:46:03 +0000
Commit:     Zac Medico <zmedico@gentoo.org>
CommitDate: 2018-08-07 18:39:26 +0000

    bin/install-qa-check.d: add new 90bad-bin-owner QA check.
    
    System executables that are not owned by root pose a security
    risk. The owner of the executable is free to modify it at any time;
    so, for example, he can change a daemon's behavior to make it
    malicious before the next time the service is started (usually by
    root).
    
    On a "normal" system, the superuser should own every system executable
    (even setuid ones, for security reasons). This commit adds a new
    install-time check that reports any such binaries with a QA
    warning. To avoid false positives, non-"normal" systems (like prefix)
    are skipped at the moment.
    
    Bug: https://bugs.gentoo.org/629398

 bin/install-qa-check.d/90bad-bin-owner | 48 ++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)