12

sudo is sometimes used to give untrusted or "semi-trusted" users the ability to perform certain tasks as root, while not giving them unlimited root access. This is usually done via an entry into /etc/sudoers, specifying which programs can be executed.

However, some programs may provide more (no pun intended) functionality than expected, such as more, less, man or find, which offer to execute other programs - most notably a shell.


Usually, which programs are safe to execute depends on knowledge of the sysadmin. Certain binaries like echo or cat are most likely safe (i.e. don't allow the user to spawn a shell), while others like the examples above are known to be exploitable.

Is there a way to assess with reasonable confidence whether or not an executable is "safe" when given sudo permissions for? Or is the only way a comprehensive source-code audit?


In response to cat not being safe: Yes, it can be used to read sensitive files as root. In some setups, this may be the intended use-case (e.g. a limited user being able to read as root, but not write).

Furthermore, comments or answers explaining to me that sudo is not the correct way to grant read permissions like this: I know. I am absolutely aware how a file-system should be structured, but due to the nature of my work, I can't influence how file-systems are structured on those servers. All I can do is to see which recommendation fixes the immediate problem. So please, don't challenge the frame of the question. I don't have an XY-problem.

15
  • 2
    True.. I think there is no real solution except analysis (source code or asm). I think this kind of site may help you for general stuff.
    – MrHeliose
    Commented Nov 27, 2019 at 16:45
  • 2
    Given the complexities of software, I think the only feasible way is going to be source code analysis, and even that is going to prone to a lot of error. Quite frankly though I think limiting sudo is more about limiting damage a neophyte user can do rather than as a security control from sophisticated users. Anecdotally, I once gave uncontrolled sudo access to a nephyte Linux user (to a vm), and she promptly destroyed the OS install through chmod. i.e. the old saying "never attribute to malice that which is adequately explained by stupidity" Commented Nov 27, 2019 at 18:27
  • 2
    Consider using Linux capabilities to only allow the needed administrative components. E.g. to allow an unprivileged user to manage network interfaces, grant them the CAP_NET_ADMIN privilege. Commented Nov 27, 2019 at 22:53
  • 3
    Fwiw, I disagree about cat being safe. sudo cat /etc/shadow Commented Nov 27, 2019 at 22:55
  • 2
    If the command was perfectly safe, then it would already be SUID and anyone (including untrusted users) could already use it. traceroute is an example. It requires root access to monitor return ICMP traffic, but is safe to let anyone use. /etc/sudoers is for users you trust enough to grant additional access, but not full discretion. It can help prevent accidental “super user mistakes”. It improves logging. A hostile sudoer generally has to deliberately bypass protections to inflict damage, where someone logged in as root can easily make an impactful mistake. Commented Nov 29, 2019 at 21:39

5 Answers 5

5
+200

In general, it's impossible to know for sure - even a seemlingly-perfectly-safe program could have vulnerabilities that mean it can be used for arbitrary actions - but here are some things to check for:

Does the program do any of the following?

  • Reveal the contents of arbitrary files or devices.
  • Copy, move, write, or delete arbitrary files.
  • Set / modify arbitrary environment variables (that will be picked up by other privileged processes), or make arbitrary changes to specific ones.
  • Invoke IOCTLs or otherwise interact with arbitrary devices.
  • Change ownership or permissions on arbitrary files.
  • Mount arbitrary file systems, or change mount options on existing ones.
  • Allow direct access to the memory of the system or of an arbitrary process (such as a debugger).
  • Allow launching arbitrary programs.

Any program that does any of those is not safe to grant a low-privileged user sudo access to. This rules out, for example, any program with the ability to specify an output file (usually via a -o or -f parameter), process an input file in any way that reveals its contents (even just via a sufficiently-informative error message about the input format being wrong), and the vast majority of script runtimes (including shells).

If you replace arbitrary in those checks with limited or specific, then you've kicked the problem down a step (or several): do any of the things that the program can do lead to such arbitrary happenings, possibly through multiple levels of indirection? For example, if your program allows the user to set a unique environment variable, that will cause a privileged program to read a different file than expected, and that different file will cause the system to allow users to mount an image file of their choice as a file system with the setuid bit respected, then you must not allow untrusted users to run that program.

However, just because a program passes all of these checks still doesn't mean it's actually safe. For example, if it performs certain network actions (such as listening on restricted ports or sending raw packets), it may be unsafe because there might be another program on the network (or on the same machine) assuming that every process capable of doing such things is owned by a trusted user - after all, those actions require root - and you've broken that assumption. Furthermore, the list of bullet points above is just stuff I thought of in a few minutes; there are almost certainly some avenues to privilege escalation that I didn't include.

Finally, as with all security questions, it depends on your threat model.

  • Is the attacker (untrusted user) local to the machine with physical access, or are they remote? It's extremely difficult to prevent a determined attacker with physical access from getting root, especially if they need to be able to (re)boot the machine without assistance, so consider what risks you're willing to accept.
  • Is the machine shared among users? Then you need to consider additional cross-user attacks, such as denial-of-service (by consuming excessive resources or rendering the machine unusable).
  • Do you require non-repudiation (the ability to prove who did something)? Then you need to make sure that you can tie any actions done through sudo to the user who did them.
  • Do you need to prevent the user from doing some things that even a non-root user usually can do (such as run arbitrary programs in their own user context even if those programs are things like games or cryptominers, or open TCP client connections to arbitrary hosts and ports)? Then you need to additionally consider the means by which you're enforcing this restriction, and prevent running as sudo any programs that might lead to a way for the user to bypass the restriction.

Etc. A truly comprehensive answer is not going to be possible here; it depends on too many things. However, I will say this: it is very hard to ensure an untrusted user, given the ability to run as root any non-trivial program (that was not explicitly designed to be safely run that way), cannot do something unexpected. Even if any one such program doesn't allow anything you find it important to prevent, it might be possible to chain multiple such programs together to achieve the attacker's goal.

5

It essentially boils down to the halting problem, you can audit the code or reverse engineer the binary, however even if there are no "features" that let you execute arbitrary commands there could still be vulnerabilities in the binary or sudo itself that could lead to arbitrary command execution as root for the enabled users.

1
  • 0-days in binaries or sudo are not in the scope of this question, but I agree with the rest.
    – user163495
    Commented Dec 2, 2019 at 7:20
1

In general what you can think, instead of binaries is on syscalls. For example cat, more and less probably they will execute the syscalls, open, read, write, close and so on. On the other hand, if the binary (for example find) can execute other binaries, with the use of -exec parameter, the syscalls implied on this process are fork, exec, mmap, etc.. that probably are the syscalls that you want to protect. On SELinux or even (secompfilter) you can have policies for specific syscalls and combine with the permissions with sudo or directly have the policies on SELinux. One good example is to ptrace binary and see the syscalls implied and then start to add or remove the permissions to these syscalls depending on your requirements

1

Basically, you will depend on the expert(s) assessing the program security having a deep knowledge of what the programs that are allowed elevated rights can do.

The above sentence includes knowledge of the libraries used by the program and the system you are using (Linux? *BSD?).

Going down to a full source-code audit may not be needed. For a well-documented program, a full read of the documentation may be enough to find unsecure options. It will also depend on the time and expertise you have available.

As is often the case, proving that a program is unsafe will be generally simple to show (once found) but proving that it is safe would be very hard.

The main problematic points would be:

  • Features unintended for your use case. From an option to view in shell to a config file that now allows embedded LUA.
  • Vulnerabilities in the program. A buffer overflow in an interactive program is generally not critical, but when run as root it becomes a path to privilege elevation.
  • Usage of many or too-complex libraries. E.g. a graphical program. You would want to reject these right away.

You will want to allow those users to run with sudo programs as simple and 'closed' as possible. If the program allows multiple options, provide them so they are fixed, if there is a configuration file, make it use one not configurable by the user, etc.

I would recommend allowing a limited shell script or short program instead of the full-fledged program.

For example, for the stated problem of ‘untrusted user needs to read absolutely any file with less’ (or emacs, vi…) I might use:

/**
 * This program when run by sudo allows reading as root an arbitrary file via less(1)
 */
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>

int sudo_var(const char* varname) {
    char *value, *endptr;
    int number;

    value = getenv(varname);
    if (!value) {
        fprintf(stderr, "Environment doesn't contain %s!\n", varname);
        exit(5);
    }

    errno = 0;
    number = strtol(value, &endptr, 10);
    if (*endptr != '\0') {
        fprintf(stderr, "Bad environment variable %s!\n", varname);
        exit(5);
    }
    if (errno || number <= 0) {
        fprintf(stderr, "Bad value for environment variable %s!\n", varname);
        exit(5);
    }
    return number;
}

int main(int argc, char **argv, char **envp) {
    gid_t gid;
    uid_t uid;
    int fd, err;
    struct stat stat_variable;
    char *less_argv[] = {"/usr/bin/less", NULL};

    if (argc < 2) {
        fprintf(stderr, "Provide the file to read\n");
        return 1;
    }
    fd = open(argv[1], O_RDONLY | O_NOCTTY);
    if (fd == -1) {
        perror("Error opening file");
        return 2;
    }

    err = fstat(fd, &stat_variable);
    if (err) {
        perror("stat error");
        return 3;
    }

    if (!S_ISREG(stat_variable.st_mode)) {
        fprintf(stderr, "Not a regular file\n");
        return 3;
    }

    err = dup2(fd, STDIN_FILENO);
    if (err) {
        perror("dup2 failed");
        return 4;
    }

    gid = sudo_var("SUDO_GID");
    uid = sudo_var("SUDO_UID");

    err = setgid(gid);
    if (err) {
        perror("Error setting group id");
        return 6;
    }

    err = setuid(uid);
    if (err) {
        perror("Error setting user id");
        return 7;
    }

    execve(less_argv[0], less_argv, envp);
    perror("exec failed");
    return 8;
}
-1

Things are not only binaries!

How can one tell if a binary is safe to give sudo permissions for to an untrusted user?

Depending on who (is to be protected, and who is able to connect), what (files, directories, file systems, sockets, etc).

I think the correct answer is to recommand to search another approach.

Nota: Last paragraph of SO question was added after my post. This is unfair! I'm sorry if my answer was not expected, but It's a correct answer. Consider learning un*x concepts!

Downvoting this answer seem not more appropriate: I've spend of my time trying to help you!

Even echo or cat could be dangerous

Depending on wich entries in filesystem could be addressed.

Under Un*x, Everything is a file! So the good question is what could be addressed via sudo command

Don't avoid less too quickly!

You could disable this behaviour by setting: SHELL=/bin/false

So with correct environment less could become a candidat!

Try simply:

SHELL=/bin/false /bin/less /somepath/to/somefile.txt

This is only a sample using less, but this will work with a lot of other tools, working in same way.

Even do offer RESTRICTED SHELL

with -r option will disallow:

  •      changing directories with cd

  •      setting or unsetting the values of SHELL, PATH, ENV, or BASH_ENV

  •      specifying command names containing /

  •      specifying  a  filename  containing  a / as an argument to the .
          builtin command

  •      specifying a filename containing a slash as an argument  to  the
          -p option to the hash builtin command

  •      importing  function  definitions  from  the shell environment at
          startup

  •      parsing the value of SHELLOPTS from  the  shell  environment  at
          startup

  •      redirecting output using the >, >|, <>, >&, &>, and >> redirect‐
          ion operators

  •      using the exec builtin command to replace the shell with another
          command

  •      adding  or  deleting builtin commands with the -f and -d options
          to the enable builtin command

  •      using the  enable  builtin  command  to  enable  disabled  shell
          builtins

  •      specifying the -p option to the command builtin command

  •      turning off restricted mode with set +r or set +o restricted.

But for accessing files, use Un*x permissions instead!

addgroup someadmins
chgroup someadmins /path/to/file
chmod g+r /path/to/file
adduser myself someadmins
adduser anna someadmins
adduser bruno someadmins
adduser charles someadmins

So sudo is no more required.

4
  • 1
    This doesn't answer the question at all.
    – user163495
    Commented Nov 29, 2019 at 18:30
  • The question was: How can I determine which binaries are safe and which ones are not? And your answer is "Here's a script that I wrote".
    – user163495
    Commented Nov 29, 2019 at 18:38
  • Input: Any binary / Output: "Safe" or "Not safe". Does it make it clearer now?
    – user163495
    Commented Nov 29, 2019 at 18:40
  • Let us continue this discussion in chat.
    – user163495
    Commented Nov 29, 2019 at 18:40

You must log in to answer this question.