Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 631882 - <net-misc/bopm-3.1.3-r4: privilege escalation via PID file manipulation
Summary: <net-misc/bopm-3.1.3-r4: privilege escalation via PID file manipulation
Status: RESOLVED FIXED
Alias: None
Product: Gentoo Security
Classification: Unclassified
Component: Vulnerabilities (show other bugs)
Hardware: All Linux
: Normal trivial (vote)
Assignee: Gentoo Security
URL:
Whiteboard: ~3 [noglsa]
Keywords:
Depends on:
Blocks:
 
Reported: 2017-09-24 00:11 UTC by Michael Orlitzky
Modified: 2017-12-18 12:45 UTC (History)
2 users (show)

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


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Michael Orlitzky gentoo-dev 2017-09-24 00:11:33 UTC
The bopm init script gives ownership of its PID file directory to its runtime user:

  start_pre() {
      checkpath -o ${BOPM_UID} -d "$(dirname "${PIDFILE}")"
  }

That can be exploited by $BOPM_UID to kill root processes, since when the service is stopped, root sends a SIGTERM to the contents of the PID file (which are controlled by $BOPM_UID).

The problem results from the fact that bopm is writing the config file, but start-stop-daemon is used to drop privileges. The privilege drop happens before the PID file gets written, so the PID file winds up being owned by the runtime user (and thus we require the parent directory to be writable by it).

You may be able to work around the issue by having OpenRC background the process and manage its PID file. If you pass the "-d" option to bopm, it will run in the foreground. You then have two cases to consider: does bopm still write its PID file? If not, great. But if so, write it somewhere where it can't cause any trouble, like /var/lib/bopm/bopm-bad-dont-use-me.pid.

With the daemon running in the foreground, you can set

  command_background=true

in the init script, and OpenRC will take care of the PID file for you. At which point it becomes appropriate to set

  pidfile="/run/${RC_SVCNAME}.pid"

and to drop the start_pre() function. Since /run is owned by root, that will avoid the vulnerability.

Two other minor issues:

  1. Use command_user="bopm:bopm" instead of start_stop_daemon args.
  2. Use command_args="-c ${CONFFILE}", because right now, the
     CONFFILE variable doesn't do anything.

I would have liked to test all this myself, but I can't get the daemon to start =P
Comment 1 Michael Orlitzky gentoo-dev 2017-09-24 00:13:53 UTC
> bopm is writing the config file

bopm is writing the *PID* file
Comment 2 Raymond Jennings 2017-10-03 05:44:13 UTC
So would I be correct to say that the init script should be properly sanitizing the BOPM_UID variable?
Comment 3 Michael Orlitzky gentoo-dev 2017-10-03 12:52:16 UTC
(In reply to Raymond Jennings from comment #2)
> So would I be correct to say that the init script should be properly
> sanitizing the BOPM_UID variable?

There's no way to sanitize it that won't lead to the same problem. All of the signals sent by OpenRC are sent as root, so if any non-root user can write to (or replace) the PID file, there's a problem.

The trick is only in figuring out how to make the PID file be owned by root, and live in a root-owned directory. Some daemons can drop privileges on their own, and then it's a matter of writing the PID file before it drops privileges.

But others (including bopm), need to be started as the unprivileged user because they can't drop privileges themselves. For that purpose, start-stop-daemon has the --user flag, and OpenRC supports the "command_user" variable. The problem with that is that, if you start the daemon as an unprivileged user, it will write the PID file as that unprivileged user, too! And that's how we got here.

Fortunately, some daemons don't even bother to write a PID file or background themselves at all. The --make-pidfile and --background flags to start-stop-daemon exist for that case, and then OpenRC manages the PID file with the proper permissions. My suggestion is to try to get bopm to run in the foreground, and then let OpenRC background it and manage its PID file.

That will fix the vulnerability, but there's still one question to be answered: will bopm continue to write its own, separate, PID file even if it's running in the foreground? If it doesn't, then great: that's what we want. But if it writes its own PID file regardless, then that PID file is going to have the unsafe permissions, so you'd have to stick it somewhere where it will be ignored.
Comment 4 Raymond Jennings 2017-10-03 16:48:43 UTC
[09:34:45] <Shentino> ok, so bug 631882
[09:35:26] <Shentino> I'll be posting a copy of this log on the bug in question.
[09:35:35] <mjo> OK
[09:35:54] <Shentino> I've never worked with stop-start-daemon before...I'm like really green at this.  I adopted the package for pmaint because it's in active use and I didn't want it getting punted
[09:35:56] <mjo> I'll behave
[09:35:58] <Shentino> LOL
[09:36:05] <Shentino> nah, I'm not big brother
[09:36:12] <Shentino> it's just that I want this discussion documented on the bug
[09:36:44] <Shentino> so let me guess...the daemon is basically writing its own pid file as a non-root process, instead of writing it as root and then dropping privileges?
[09:37:20] <mjo> Well, the daemon is writing a PID file as whatever user the daemon is running as. Since the daemon can't drop privileges itself, that would typically be root, and everything would be fine.
[09:37:46] <mjo> But, someone noticed that the daemon doesn't actually need to run as root, and they've told start-stop-daemon to launch it as $BOPM_UID instead
[09:38:13] <mjo> That results in a PID file owned by $BOPM_UID, and not root -- which is a problem because root trusts the PID file
[09:38:23] <Shentino> I see, I think
[09:38:48] <Shentino> but...why is that a problem?  I mean are we worried that someone else running as bopm's uid could hijack the pid file?
[09:39:29] <Shentino> I'm assuming here that BOPM_UID itself being corrupted isn't the concern
[09:39:52] <mjo> The unprivileged bopm user can put whatever he wants into the PID file. Then when the bopm service is stopped, root will send a SIGTERM to the contents of the PID file. That lets the unprivileged user (for example) reboot the machine, kill off SSHD, disable the firewall...
[09:40:32] <Shentino> Ok, so basically this is a way that would let a buggy (or hacked) bopm process (or indeed anyone else running as bopm) to hijack the pid file and use it to attack the rest of the system?
[09:40:50] <mjo> Yeah, it's not serious, but he could be really annoying
[09:41:07] <mjo> If someone kills off my server's SSHD processes, I have to drive to work to fix it
[09:41:22] <Shentino> I guess I didn't see the urgency.  I agree it needs fixed
[09:41:37] <mjo> (meanwhile, the attacker would be thinking of other bad things to do, and I would be helpless to stop it)
[09:41:50] <Shentino> but...it would seem that to exploit this, first you'd have to exploit bopm itself, or hijack bopm's uid somehow
[09:42:10] <Shentino> is that what the concern is?
[09:42:13] <mjo> True, but if that's not possible, then why run as an unprivileged user anyway?
[09:42:22] <Shentino> I don't know
[09:42:29] <Shentino> I'd need to read up more on bopm
[09:42:48] <mjo> Running as an unprivileged user is a "just in case" measure to prevent a hacked process from doing too much damage
[09:42:53] <Shentino> in the meantime though, in principle I completely agree.  Bopm should be properly sandboxed anyway so that we don't HAVE to trust it.
[09:42:56] <Shentino> exactly
[09:43:37] <Shentino> anyway I have stuff to do irl, but in the meantime if you could explain start-stop-daemon in a nutshell that would help.  I'm completely green here.  Or what manpage to study.
[09:44:03] <Shentino> like it doesn't appear to be directly called in the initscript, so my guess is that the interpreter of that script is doing it as some kind of default action (similiar to default ebuild steps)
[09:44:31] <mjo> Well, start-stop-daemon was originally forked from Debian and for old-fashioned daemons, just saves you a little bit of boilerplate
[09:45:01] <mjo> But for newfangled systemd-style daemons, it also has the ability to supervise a daemon, and put the daemon into the background
[09:45:41] <mjo> Traditionally, daemons will background themselves, drop privileges, close their file descriptors, and create a PID file (not in that order though)
[09:46:05] <mjo> These days, a lot of daemons run in the foreground and don't do any of that stuff -- they rely on systemd or another modern init system to do all that stuff for them
[09:46:09] <Shentino> heh
[09:46:25] <Shentino> anyway so what actually calls start-stop-daemon in bopm's initscript?
[09:46:25] <mjo> start-stop-daemon can do either, depending on the type of daemon you have
[09:46:47] <mjo> The default start() and stop() functions in OpenRC call start-stop-daemon
[09:47:03] <Shentino> aha
[09:47:08] <mjo> try "man openrc-run" for a lengthy overview
[09:47:11] <Shentino> thanks
[09:47:16] <Shentino> that's what I needed
[09:47:31] <Shentino> I'll study that when I can.  In the meantime, thanks for flagging the issue.
[09:47:55] <Shentino> I hope I can get this patched soon...would give me credit for github on hacktoberfest for a new tshirt :)
[09:47:56] <mjo> Sure thing. I'm also working on an "init script tips" document at https://github.com/orlitzky/openrc/blob/protips/PROTIPS.md
[09:47:59] <Shentino> thanks
Comment 5 Raymond Jennings 2017-10-03 20:47:05 UTC
[09:48:43] <mjo> My suggestion on the bug is basically, make bopm act like a new-style foreground daemon, and then let OpenRC do all the work
[09:49:05] <Shentino> I agree
[09:49:10] <Shentino> I'll read bopm's manpage as well
[09:49:25] <mjo> In theory that's easy, but in practice, most projects half-ass one or both modes of operation and you have to work around the weirdness
[09:49:50] <Shentino> I think that we might be able to trick bopm into writing its pidfile into a garbage directory
[09:50:05] <Shentino> gotta run
[09:50:13] <mjo> Sure, good luck
Comment 6 Raymond Jennings 2017-10-03 21:41:27 UTC
I checked the provided config file for bopm, and it can specify the PID file.

So...for now, we can have BOPM write its own pid file to a junk folder

tenative solution

1.  Have the initscript create a root-owned, world writable, sticky directory called "/run/bopm/junk
2.  Have bopm's config file tell bopm to write it's pid to "/run/bopm/junk/bopm-bad-pid-file.pid"
3.  Have start-stop-daemon write its own pid file for bopm to /run/bopm/bopm.pid

Does this work?
Comment 7 Michael Orlitzky gentoo-dev 2017-10-03 22:40:09 UTC
(In reply to Raymond Jennings from comment #6)
> 
> 1.  Have the initscript create a root-owned, world writable, sticky
> directory called "/run/bopm/junk
> 2.  Have bopm's config file tell bopm to write it's pid to
> "/run/bopm/junk/bopm-bad-pid-file.pid"
> 3.  Have start-stop-daemon write its own pid file for bopm to
> /run/bopm/bopm.pid
> 
> Does this work?

You have to use a different directory for the "junk" and "real" PID files. If you can write to a directory, then you can replace files in that directory -- even if you don't own them. So the unprivileged $BOPM_UID might not be able to write to bopm.pid above, but he can still replace it entirely.

The directory that holds the "junk" PID file only needs to be writable by $BOPM_UID (not world-writable).

And finally, there's a step 2.5: pass "-d" to bopm so that it doesn't fork itself into the background before start-stop-daemon can do it. Afterwards, there's still the possibility that bopm is smart enough not to write the PID file when you pass it "-d" on the command-line. If we're really lucky, that's the case, and we don't have to worry about the junk PID file at all.
Comment 8 Michael Orlitzky gentoo-dev 2017-10-03 22:42:03 UTC
> You have to use a different directory for the "junk" and "real" PID files.

Oh, sorry! I missed that you're using a "junk" subdirectory of /run/bopm for the junk PID file. That should be fine, so long as /run/bopm is itself owned by root.
Comment 9 Raymond Jennings 2017-10-03 23:15:58 UTC
Yeah, that's what I meant.

Basically,
/run/bopm, root, 0755, written by initscript
/run/bopm/junk, bopm, 0755, written by initscript
/run/bopm/junk/bopm-bad.pid, bopm, whatever, written by bopm
/run/bopm/bopm.pid, root, 0644, written by start-stop-daemon
Comment 10 Michael Orlitzky gentoo-dev 2017-10-03 23:24:13 UTC
(In reply to Raymond Jennings from comment #9)
> 
> /run/bopm, root, 0755, written by initscript
> /run/bopm/junk, bopm, 0755, written by initscript
> /run/bopm/junk/bopm-bad.pid, bopm, whatever, written by bopm
> /run/bopm/bopm.pid, root, 0644, written by start-stop-daemon


*thumbs up*
Comment 11 Raymond Jennings 2017-10-15 02:09:44 UTC
PR filed on github:

https://github.com/gentoo/gentoo/pull/5924
Comment 12 Larry the Git Cow gentoo-dev 2017-10-15 21:11:26 UTC
The bug has been referenced in the following commit(s):

https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=b268b306966b45a54da8a5573e09915e0ca92a96

commit b268b306966b45a54da8a5573e09915e0ca92a96
Author:     Raymond Jennings <shentino@gmail.com>
AuthorDate: 2017-10-12 06:50:58 +0000
Commit:     Patrice Clement <monsieurp@gentoo.org>
CommitDate: 2017-10-15 21:11:03 +0000

    net-misc/bopm: fix security bug with pid file.
    
    In theory, someone who explots a hacked bopm could then use it to
    attack root owned processes.
    
    This puts the bopm-written PID file into a disposable junk directory
    and lets start-stop-daemon do all the grunt work.
    
    Bug: https://bugs.gentoo.org/631882
    Closes: https://github.com/gentoo/gentoo/pull/5924

 .../bopm/{bopm-3.1.3-r3.ebuild => bopm-3.1.3-r4.ebuild}  |  3 ++-
 .../bopm/files/bopm-3.1.3-quarantine-bad-pid-file.patch  | 16 ++++++++++++++++
 net-misc/bopm/files/{bopm.init.d-r1 => bopm.init.d-r2}   |  4 +++-
 3 files changed, 21 insertions(+), 2 deletions(-)}
Comment 13 Raymond Jennings 2017-12-18 05:50:09 UTC
ping?
Comment 14 Christopher Díaz Riveros (RETIRED) gentoo-dev Security 2017-12-18 12:45:52 UTC
(In reply to Raymond Jennings from comment #13)
> ping?

Thanks, closing as RESOLVED 

No GLSA for this report.