Wednesday 15 December 2010

Not impossible: setuid shell scripting

I often come across the age old question of "why can't I setuid a Bash script?".  Well the simple one word answer is "security".  Plain and simple, having a script that is potentially modifiable or susceptible to script injection, either through parameters or through the environment, is a major security flaw.  However, there are ways of making a script root executable in a controlled manner, that ensures a clean environment.

Since the introduction of 'sudo', it is possible to execute any script as root by simply replacing the shebang with the following:

#!/usr/bin/sudo /bin/bash

However, this is extremely insecure since you would be handing root privileges to /bin/bash from within the sudoers to anybody with access rights to run bash through sudo.  Thus this would be innevitable:

[blee@dragon:~]$ sudo /bin/bash --login
[root@dragon:~]# id
uid=0(root) gid=0(root) groups=0(root)

So how do you permit bash to be executed as root from the shebang, whilst maintaining control over what can actually be executed?  The answer is to write a more intricate sudo rules to enable us to execute these setuid scripts.  First a User_Alias is required to provide a list of all the users permitted to execute certain scripts:

User_Alias ROOT_SUID_USERS = blee, cnorris

Next we need to know what scripts can be run as root.

Cmd_Alias ROOT_SUID_SCRIPTS = /usr/bin/myscript

Next we want to ensure that the environment is reset when invoking these commands:

Defaults!ROOT_SUID_SCRIPTS             env_reset

Next we put the two together:

ROOT_SUID_USERS        ALL = (root) NOPASSWD: ROOT_SUID_SCRIPTS

Now, /usr/bin/myscript is permissibly executable as root by the users blee and cnorris.  However, since sudo is invoked from the calling script's shebang, we need to somehow invoke bash in a safe way, otherwise we would just end up in a loop, with sudo being invoked by itself from /usr/bin/myscript.  So what we do is prefix each of the scripts in the /bin/bash invocation, which is safe, since we are saying that /bin/bash can be invoked by sudo providing it is immediately followed by the /usr/bin/myscript argument:

Cmd_Alias ROOT_SUID_SCRIPTS = \
    /bin/bash /usr/bin/myscript, \
    /bin/bash /usr/bin/myotherscript

In /usr/bin/myscript, we replace the shebang as follows:

#!/usr/bin/sudo /bin/bash

Now, sudo will invoke /bin/bash as root given the rule, providing cnorris or blee are the users executing the script.  Here are the test results:

Before we add anything to the sudoers file, but with our shebang in place:

[blee@dragon:~]$ myscript


We trust you have received the usual lecture from the local System
Administrator.  It usually boils down to these three things:


    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.


[sudo] password for blee:
Sorry, user blee is not allowed to execute '/bin/bash /usr/bin/myscript' as root on dragon.

So lets add the sudoers configuration and try again:

[blee@dragon:~]$ myscript
myscript: Demonstrating setuid shell scripting:
uid=0(root) gid=0(root) groups=0(root)


[tjones@dragon:~]$ myscript


We trust you have received the usual lecture from the local System
Administrator.  It usually boils down to these three things:

    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.

[sudo] password for tjones:
Sorry, user tjones is not allowed to execute '/bin/bash /usr/bin/myscript' as root on dragon.


Just to prove that /bin/bash cannot be exploited through this sudo rule:

[blee@dragon:~]$ sudo /bin/bash --login


We trust you have received the usual lecture from the local System
Administrator.  It usually boils down to these three things:

    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.

[sudo] password for blee:
Sorry, user blee is not allowed to execute '/bin/bash --login' as root on dragon.

[blee@dragon:~]$ sudo /bin/bash

We trust you have received the usual lecture from the local System
Administrator.  It usually boils down to these three things:

    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.

[sudo] password for blee:
Sorry, user blee is not allowed to execute '/bin/bash' as root on dragon.

But, executing myscript the by bypassing the shebang is fine:

[blee@dragon:~]$ sudo /bin/bash /usr/bin/myscript
myscript: Demonstrating setuid shell scripting:
uid=0(root) gid=0(root) groups=0(root)

To summarise, there we have blee running myscript and assuming root privileges for the life of the script.  Obviously, it does rely on the author of the scripts being run as root, to write the scripts securely, so there will possibly be an opportunity for exploitation if scripts are written sloppily.  Also, the permissions of these scripts must be as such that they are owned and writeable only by root!  Any modification to the file permissions that would allow anybody else write access creates a window of opportunity for someone to modify the contents as such it would grant them a root shell, providing they have permission to execute it and be granted root privileges from sudo.

As sudo suggests:

It usually boils down to these three things:

    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.




No comments:

Post a Comment