3

I cannot work out how to disable shell escapes for any command, so that I could (this is an example!) do something like:

sudo-user ALL=(ALL) NOEXEC: NOPASSWD: ALL

The sudoers manual says this:

A command may have zero or more tags associated with it. There are eight possible tag values, NOPASSWD, PASSWD, NOEXEC, EXEC, SETENV, NOSETENV, LOG_INPUT, NOLOG_INPUT, LOG_OUTPUT and NOLOG_OUTPUT. Once a tag is set on a Cmnd, subsequent Cmnds in the Cmnd_Spec_List, inherit the tag unless it is overridden by the opposite tag (i.e.: PASSWD overrides NOPASSWD and NOEXEC overrides EXEC)

But this doesn't describe how to set more than one flag. Using a comma doesn't appear to work (it treats the second tag as an alias)

Better still, I'd like to set a default, so the line above would be:

defaults NOEXEC     
sudo-user ALL=(ALL) NOPASSWD: ALL

I know that sudo can be compiled with noexec, but that's not practical in this instance. I also know that this isn't the whole answer - it's possible to subvert by setting LD_PRELOAD appropriately, but it's a start.

Sudo version:

Sudo version 1.8.19p2
Sudoers policy plugin version 1.8.19p2
Sudoers file grammar version 45
Sudoers I/O plugin version 1.8.19p2
On RHEL 7.5
  • 1
    If you are worried about shell escapes, then you should not give sudo. – Rui F Ribeiro Oct 8 '18 at 12:00
  • Yeah, that's my position too, TBH. I suspect though that sudo offers enough security theatre for the powers that be to be happy. – Graham Nicholls Oct 8 '18 at 12:47
  • Noexec is advisory. cp /usr/lib/klibc/bin/sh /tmp/martian ; sudo /tmp/martian Go ahead; try it. It ignores your noexec directive even though you can see that it is inherited because child processes it spawns inherit it. – Joshua Oct 8 '18 at 21:26
17

Don't do this.

You're giving the sudo-user the ability to run any commands as any user. That includes the ability to modify /etc/sudoers using any tool that does not require executing sub-processes. You may be thinking that excludes visudo, but strictly speaking, you don't need visudo to edit /etc/sudoers.

Like, for example:

sudo-user$ sudo sed --in-place -e '/^sudo-user/s/NOEXEC://' /etc/sudoers

...and the user has just removed the NOEXEC: restriction.

A restriction that the user can remove at will is not a true restriction.


Also, NOEXEC: may cause problems. It does not just prevent shell escapes: it prevents the program executed with sudo from directly starting any other programs. This can have a lot of consequences.

For example, if your sudo-user stops and later restarts the cron daemon (e.g. to stop scheduled jobs for the duration of some maintenance), the restarted cron daemon will be unable to actually execute any scheduled jobs because of the NOEXEC: restriction.

NOEXEC: exists so that the sysadmin can apply it to carefully selected programs that are found to be able to perform their task without exec()ing any sub-processes. Applying it blindly to everything is going to cause problems.


But if you absolutely must, here's how.

From the sudoers(5) man page:

User specification
 User_Spec ::= User_List Host_List '=' Cmnd_Spec_List \
               (':' Host_List '=' Cmnd_Spec_List)*

 Cmnd_Spec_List ::= Cmnd_Spec |
                    Cmnd_Spec ',' Cmnd_Spec_List

 Cmnd_Spec ::= Runas_Spec? SELinux_Spec? Tag_Spec* Cmnd

 Runas_Spec ::= '(' Runas_List? (':' Runas_List)? ')'

 SELinux_Spec ::= ('ROLE=role' | 'TYPE=type')

 Tag_Spec ::= ('EXEC:' | 'NOEXEC:' | 'FOLLOW:' | 'NOFOLLOW' |
               'LOG_INPUT:' | 'NOLOG_INPUT:' | 'LOG_OUTPUT:' |
               'NOLOG_OUTPUT:' | 'MAIL:' | 'NOMAIL:' | 'PASSWD:' |
               'NOPASSWD:' | 'SETENV:' | 'NOSETENV:')

This is a detailed description on how to construct a user specification line for the sudoers file. It may be a little tedious to read, but it does hold the information you need.

Let's work it out using your example line:

sudo-user ALL=(ALL) NOEXEC: NOPASSWD: ALL

The entire line is known as the user specification, or User_Spec.

It breaks down as follows:

  • User_List has just one user in your example: sudo-user
  • Host_List has just one entry, ALL
  • Cmnd_Spec_List in your example is (ALL) NOEXEC: NOPASSWD: ALL
  • there are no further : Host_List = Cmnd_Spec_List units on this line (the asterisk after parentheses suggests there might be zero or more additional units like this.)

Your Cmnd_Spec_List has no commas, so it has just one Cmnd_Spec.

Cmnd_Spec breaks down into:

  • optional Runas_Spec: in your case, (ALL)
  • optional SELinux_Spec, which does not exist in your example
  • zero or more Tag_Specs, which is the part you want to know about
  • Cmnd, the command, which is ALL in your case.

And a single Tag_Spec is just one of the listed keywords with a colon at the end, with no comma, space or other delimiter explicitly listed. The Cmnd_Spec ::= line tells us exactly where to put spaces on that line. Since there is no instruction to put spaces or any other delimiters between tags, don't do that.

So, just put the tags one after the other, like this:

sudo-user ALL=(ALL) NOEXEC:NOPASSWD: ALL
  • I was kind of hoping to avoid the "Don't do this" responses with the "this is an example" text, but I guess it was always inevitable. Nevertheless, thank you for the rest of the post which is very informative. I tried putting the two tags together, but the second was interpreted as a command alias. I notice that you have no space between the two, which was, I suspect my problem. Anyway, I upvoted you and thanks again. What's your take on @Ruis' comment, which was the conclusion I was coming to? – Graham Nicholls Oct 8 '18 at 13:01
  • 2
    I tend to agree with him: if you allow all commands, preventing the shell escapes is the least of your worries. Although I'd say that noexec may have its place when you need to grant some user(s) permission to use some specific tool that requires high privileges and just happens to include shell escape functionality.If you plan to use sudo as you describe to satisfy a requirement to log all privileged commands, see the auditd suggestion in this Server Fault post. – telcoM Oct 8 '18 at 13:21
  • Actually, @Rui 's comment was stronger that that - don't use sudo at all if you're concerned about shell escapes - any comment on that? – Graham Nicholls Oct 8 '18 at 14:57
  • 2
    If you want to give an user a form of limited root access for the purpose of running some program, it is your responsibility as a sysadmin to carefully go through the features of the program and ensure that it contains no shell escapes. If it contains a shell escape, then - after careful testing - you can maybe allow it with NOEXEC. Just slapping NOEXEC on everything is the wrong and dangerous kind of lazy way. – telcoM Oct 8 '18 at 15:32
5

You don't need a tag for this, as noexec is also a boolean flag to the Default entry line:

noexec

If set, all commands run via sudo will behave as if the NOEXEC tag has been set, unless overridden by a EXEC tag. See the description of NOEXEC and EXEC below as well as the Preventing shell escapes section at the end of this manual. This flag is off by default.

So all you need is

Defaults        noexec

This will have the same problems that telcoM notes in their answer . It will prevent visudo from running (as that needs to start a separate editor process), so it will be harder to undo the change afterwards.

0

Combining the two answers so far given,how about this:

Defaults noexec
root    ALL=(ALL)       ALL
%wheel  ALL=(ALL)       ALL
sudo-user ALL=(ALL)     EXEC:NOPASSWD:  ALL
fred ALL=(ALL)          NOPASSWD:  user_allowed_commands

Actually, I am proposing to give unfettered access to the sudo-user. This seems to be the recommended method (in Ubuntu et al, at least), rather than using root. I'm already auditing the /etc/sudoers directory as it happens. This allows fred to run the list of commands in "user_allowed_commands" (assuming that fork/exec is not needed, which seems unlikely, TBH - or am I misreading what noexec is doing?)

I'd like to add this from @telcoM : A restriction that the user can remove at will is not a true restriction.

In all the SELinux implementations I see, it's not immutable, so any user can run setenforce permissive, and all restrictions are removed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.