Bug 1372830 (CVE-2016-7032) - CVE-2016-7032 sudo: noexec bypass via system() and popen()
Summary: CVE-2016-7032 sudo: noexec bypass via system() and popen()
Keywords:
Status: CLOSED ERRATA
Alias: CVE-2016-7032
Product: Security Response
Classification: Other
Component: vulnerability
Version: unspecified
Hardware: All
OS: Linux
medium
medium
Target Milestone: ---
Assignee: Red Hat Product Security
QA Contact:
URL:
Whiteboard:
Depends On: 1391937 1391938 1391939 1391940
Blocks: 1372831
TreeView+ depends on / blocked
 
Reported: 2016-09-02 19:56 UTC by Kurt Seifried
Modified: 2021-02-17 03:23 UTC (History)
10 users (show)

Fixed In Version: sudo 1.8.15
Doc Type: If docs needed, set a value
Doc Text:
It was discovered that the sudo noexec restriction could have been bypassed if application run via sudo executed system() or popen() C library functions with a user supplied argument. A local user permitted to run such application via sudo with noexec restriction could use this flaw to execute arbitrary commands with elevated privileges.
Clone Of:
Environment:
Last Closed: 2016-12-06 11:56:17 UTC
Embargoed:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Red Hat Product Errata RHSA-2016:2872 0 normal SHIPPED_LIVE Moderate: sudo security update 2016-12-06 16:06:44 UTC

Description Kurt Seifried 2016-09-02 19:56:52 UTC
Florian Weimer of Red Hat reports:


the sudoers manual page says this:

EXEC and NOEXEC

If sudo has been compiled with noexec support and the underly‐
ing operating system supports it, the NOEXEC tag can be used to
prevent a dynamically-linked executable from running further
commands itself.

In the following example, user aaron may run /usr/bin/more and
/usr/bin/vi but shell escapes will be disabled.

aaron shanty = NOEXEC: /usr/bin/more, /usr/bin/vi

And:

To enable noexec for a command, use the NOEXEC tag as
documented in the User Specification section above.
Here is that example again:

aaron shanty = NOEXEC: /usr/bin/more, /usr/bin/vi

This allows user aaron to run /usr/bin/more and
/usr/bin/vi with noexec enabled. This will prevent
those two commands from executing other commands (such
as a shell). If you are unsure whether or not your
system is capable of supporting noexec you can always
just try it out and check whether shell escapes work
when noexec is enabled.

However, the filtering DSO does not intercept all glibc functions which 
allow to spawn a shell. At least popen, system, and wordexp are missing:

Show quoted text
0000000000000c80 T execl
0000000000000cc0 T _execl
0000000000000d00 T __execl
0000000000000d40 T execle
0000000000000d80 T _execle
0000000000000dc0 T __execle
0000000000000e00 T execlp
0000000000000e40 T _execlp
0000000000000e80 T __execlp
0000000000000ec0 T exect
0000000000000f00 T _exect
0000000000000f40 T __exect
0000000000000f80 T execv
0000000000000fc0 T _execv
0000000000001000 T __execv
00000000000011c0 T execve
0000000000001200 T _execve
0000000000001240 T __execve
0000000000001040 T execvp
0000000000001080 T _execvp
00000000000010c0 T __execvp
0000000000001100 T execvP
0000000000001140 T _execvP
0000000000001180 T __execvP
0000000000001280 T execvpe
00000000000012c0 T _execvpe
0000000000001300 T __execvpe
0000000000001340 T fexecve
0000000000001380 T _fexecve
00000000000013c0 T __fexecve
0000000000001480 T __posix_spawn
0000000000001440 T _posix_spawn
0000000000001400 T posix_spawn
0000000000001540 T __posix_spawnp
0000000000001500 T _posix_spawnp
00000000000014c0 T posix_spawnp

The source file src/sudo_noexec.c contains this comment:

/*
* Dummy versions of the execve() family of syscalls. We don't need
* to stub out all of them, just the ones that correspond to actual
* system calls (which varies by OS). Note that it is still possible
* to access the real syscalls via the syscall() interface but very
* few programs actually do that.
*/

This is wrong. Interposing execve does not override internal calls to 
execve within glibc.

The right fix is to set a seccomp filter which blocks execve in the DSO 
(after enabling PR_SET_NO_NEW_PRIVS), rather than attempting to play 
catch-up with glibc.

Comment 1 Kurt Seifried 2016-09-02 19:56:58 UTC
Acknowledgments:

Name: Florian Weimer (Red Hat)

Comment 2 Tomas Hoger 2016-09-07 12:09:22 UTC
Apparently upstream is aware of the problem and addressed system() and popen() cases about a year ago:

https://www.sudo.ws/devel.html#1.8.15rc1
https://www.sudo.ws/repos/sudo/rev/58a5c06b5257
https://www.sudo.ws/repos/sudo/rev/a826cd7787e9

Comment 3 Florian Weimer 2016-09-07 12:33:52 UTC
(In reply to Tomas Hoger from comment #2)
> Apparently upstream is aware of the problem and addressed system() and
> popen() cases about a year ago:
> 
> https://www.sudo.ws/devel.html#1.8.15rc1
> https://www.sudo.ws/repos/sudo/rev/58a5c06b5257
> https://www.sudo.ws/repos/sudo/rev/a826cd7787e9

Ahh, maybe that's why I was vaguely familiar with this issue.  True blocking still needs a seccomp filter, though.

Comment 4 Tomas Hoger 2016-09-08 12:09:03 UTC
The sudo version in Red Hat Enterprise Linux 5 also lacks this commit:

https://www.sudo.ws/repos/sudo/rev/cbaa1d4b0f8a

Blocking wrappers for posix_spawn() and posix_spawnp() are relevant.  exect() seems BSD-specific API, and execvpe() was only added in glibc 2.11, while Red Hat Enterprise Linux 5 includes glibc 2.5.

Comment 5 Tomas Hoger 2016-09-08 12:52:12 UTC
(In reply to Kurt Seifried from comment #0)
> The right fix is to set a seccomp filter which blocks execve in the DSO 
> (after enabling PR_SET_NO_NEW_PRIVS), rather than attempting to play 
> catch-up with glibc.

As far as I can see, such seccomp approach would only be viable on Red Hat Enterprise Linux 7 or later.  I'm also unsure if sudo upstream would be willing to write the code.  Do you or sudo maintainer have capacity to implement this and get is merged upstream?

Comment 7 Florian Weimer 2016-09-08 18:47:14 UTC
(In reply to Tomas Hoger from comment #5)
> (In reply to Kurt Seifried from comment #0)
> > The right fix is to set a seccomp filter which blocks execve in the DSO 
> > (after enabling PR_SET_NO_NEW_PRIVS), rather than attempting to play 
> > catch-up with glibc.
> 
> As far as I can see, such seccomp approach would only be viable on Red Hat
> Enterprise Linux 7 or later.  I'm also unsure if sudo upstream would be
> willing to write the code.  Do you or sudo maintainer have capacity to
> implement this and get is merged upstream?

A while back I wrote:

  https://github.com/fweimer/noexecve/blob/master/noexecve.c

It's not 100% complete because it does not deal with multi-arch system calls.  On x86_64, a program could still evade this by jumping to short mode and doing the i386 syscalls (which have different numbers), or call the x32 syscalls.  In most cases, this will need some sort of code execution exploit in the target process because the process will not expose this sort of capability to the end user.

Comment 8 Tomas Hoger 2016-09-09 07:55:33 UTC
Would this also need other tweaks for other non-x86 architectures?  Would use of libseccomp be recommended to hide platform specific details?

Comment 9 Florian Weimer 2016-09-09 08:24:10 UTC
(In reply to Tomas Hoger from comment #8)
> Would this also need other tweaks for other non-x86 architectures?

Other architectures may have different forms of compatibility system calls, yes.

> Would use of libseccomp be recommended to hide platform specific details?

I don't know if it handles multi-arch system calls in the way needed.  I have asked Paul Moore.

Comment 11 Tomas Hoger 2016-10-11 09:46:45 UTC
The following changes were applied upstream for inclusion in 1.8.18p1:

* Wrapper for wordexp() was added to sudo_noexec.so which forces the use of WRDE_NOCMD flag in wordexp().

https://www.sudo.ws/repos/sudo/rev/e7d09243e51b
https://www.sudo.ws/repos/sudo/rev/7b8357b0a358
https://www.sudo.ws/repos/sudo/rev/167a518d8129

NEWS file entry:

 * When sudo_noexec.so is used, the WRDE_NOCMD flag is now added
   if the wordexp() function is called.  This prevents commands
   from being run via wordexp() without disabling it entirely.

* seccomp support was added to sudo_noexec.so.

https://www.sudo.ws/repos/sudo/rev/59d76bdc0f0c
https://www.sudo.ws/repos/sudo/rev/5d88d7cda853

NEWS file entry:

 * On Linux systems, sudo_noexec.so now uses a seccomp filter to
   disable execute access if the kernel supports seccomp.  This is
   more robust than the traditional method of using stub functions
   that return an error.

Comment 12 Tomas Hoger 2016-10-14 13:46:24 UTC
As system()/popen() and wordexp() were fixed in different versions, the wordexp() case will be tracked under separate CVE-2016-7076 - see bug 1384982.

Comment 13 Tomas Hoger 2016-10-27 18:03:57 UTC
Public now via upstream advisory.

External References:

https://www.sudo.ws/alerts/noexec_bypass.html

Comment 19 errata-xmlrpc 2016-12-06 11:06:59 UTC
This issue has been addressed in the following products:

  Red Hat Enterprise Linux 6
  Red Hat Enterprise Linux 7

Via RHSA-2016:2872 https://rhn.redhat.com/errata/RHSA-2016-2872.html


Note You need to log in before you can comment on or make changes to this bug.