Why is SUID disabled for shell scripts but not for binaries?
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
1
down vote
favorite
While I understand the idea of SUID is to let an unprivileged user run a program as a privileged user, I have found that SUID usually doesn't work on a shell script without some workarounds. My question is, I don't really understand the dichotomy between a shell script and a binary program. It seems that whatever you can do with a shell script, you can also do it with C and compile it into a binary. If SUID is not secure for a shell script, then it's also not secure for binaries. So why would shell scripts but not binaries be prohibited from using SUID?
linux privilege-escalation
add a comment |Â
up vote
1
down vote
favorite
While I understand the idea of SUID is to let an unprivileged user run a program as a privileged user, I have found that SUID usually doesn't work on a shell script without some workarounds. My question is, I don't really understand the dichotomy between a shell script and a binary program. It seems that whatever you can do with a shell script, you can also do it with C and compile it into a binary. If SUID is not secure for a shell script, then it's also not secure for binaries. So why would shell scripts but not binaries be prohibited from using SUID?
linux privilege-escalation
add a comment |Â
up vote
1
down vote
favorite
up vote
1
down vote
favorite
While I understand the idea of SUID is to let an unprivileged user run a program as a privileged user, I have found that SUID usually doesn't work on a shell script without some workarounds. My question is, I don't really understand the dichotomy between a shell script and a binary program. It seems that whatever you can do with a shell script, you can also do it with C and compile it into a binary. If SUID is not secure for a shell script, then it's also not secure for binaries. So why would shell scripts but not binaries be prohibited from using SUID?
linux privilege-escalation
While I understand the idea of SUID is to let an unprivileged user run a program as a privileged user, I have found that SUID usually doesn't work on a shell script without some workarounds. My question is, I don't really understand the dichotomy between a shell script and a binary program. It seems that whatever you can do with a shell script, you can also do it with C and compile it into a binary. If SUID is not secure for a shell script, then it's also not secure for binaries. So why would shell scripts but not binaries be prohibited from using SUID?
linux privilege-escalation
linux privilege-escalation
asked 2 hours ago
Cyker
686510
686510
add a comment |Â
add a comment |Â
2 Answers
2
active
oldest
votes
up vote
3
down vote
There is a race condition inherent to the way shebang (#!
) is typically implemented:
- The kernel opens the executable, and finds that it starts with
#!
. - The kernel closes the executable and opens the interpreter instead.
- The kernel inserts the path to the script to the argument list (as
argv[1]
), and executes the interpreter.
If setuid scripts are allowed with this implementation, an attacker can invoke an arbitrary script by creating a symbolic link to an existing setuid script, executing it, and arranging to change the link after the kernel has performed step 1 and before the interpreter gets around to opening its first argument. For this reason, all modern unices ignore the setuid bit when they detect a shebang.
One way to secure this implementation would be for the kernel to lock the script file until the interpreter has opened it (note that this must prevent not only unlinking or overwriting the file, but also renaming any directory in the path). But unix systems tend to shy away from mandatory locks, and symbolic links would make a correct lock feature especially difficult and invasive. I don't think anyone does it this way.
A few unix systems implement secure setuid shebang using an additional feature: the path /dev/fd/N
refers to the file already opened on file descriptor N (so opening /dev/fd/N
is roughly equivalent to dup(N)
).
- The kernel opens the executable, and finds that it starts with
#!
. Let's say the file descriptor for the executable is 3. - The kernel opens the interpreter.
- The kernel inserts
/dev/fd/3
the argument list (asargv[1]
), and executes the interpreter.
All modern unix variants including Linux implement /dev/fd
, but most do not allow setuid scripts. OpenBSD, NetBSD and MacÃÂ OSÃÂ X support it if you enable a non-default kernel setting. On Linux, people have written patches to allow it but those patches never got merged. Sven Mascheck's shebang page has a lot of information on shebang across unices, including setuid support.
In addition, programs running with elevated privileges have inherent risks that are typically harder to control in higher-level programming languages unless the interpreter was specifically designed for it. The reason is that the programming language runtime's initialization code may perform actions with elevated privileges, based on data that's inherited from the lower-privilege caller, before the program's own code has had the opportunity to sanitize this data. The C runtime does very little for the programmer, so C programs have a better opportunity to take control and sanitize data before anything bad can happen.
Let's assume you've managed to make your program run as root, either because your OS supports setuid shebang or because you've used a native binary wrapper (such as sudo
). Have you opened a security hole? Maybe. The issue here is not about interpreted vs compiled programs. The issue is whether your runtime system behaves safely if executed with privileges.
Any dynamically linked native binary executable is in a way interpreted by the dynamic loader (e.g.
/lib/ld.so
), which loads the dynamic libraries required by the program. On many unices, you can configure the search path for dynamic libraries through the environment (LD_LIBRARY_PATH
is a common name for the environment variable), and even load additional libraries into all executed binaries (LD_PRELOAD
). The invoker of the program can execute arbitrary code in that program's context by placing a specially-craftedlibc.so
in$LD_LIBRARY_PATH
(amongst other tactics). All sane systems ignore theLD_*
variables in setuid executables.In shells such as sh, csh and derivatives, environment variables automatically become shell parameters. Through parameters such as
PATH
,IFS
, and many more, the invoker of the script has many opportunities to execute arbitrary code in the shell scripts's context. Some shells set these variables to sane defaults if they detect that the script has been invoked with privileges, but I don't know that there is any particular implementation that I would trust.Most runtime environments (whether native, bytecode or interpreted) have similar features. Few take special precautions in setuid executables, though the ones that run native code often don't do anything fancier than dynamic linking (which does take precautions).
Perl is a notable exception. It explicitly supports setuid scripts in a secure way. In fact, your script can run setuid even if your OS ignored the setuid bit on scripts. This is because perl ships with a setuid root helper that performs the necessary checks and reinvokes the interpreter on the desired scripts with the desired privileges. This is explained in the perlsec manual. It used to be that setuid perl scripts needed
#!/usr/bin/suidperl -wT
instead of#!/usr/bin/perl -wT
, but on most modern systems,#!/usr/bin/perl -wT
is sufficient.
Note that using a native binary wrapper does nothing in itself to prevent these problems. In fact, it can make the situation worse, because it might prevent your runtime environment from detecting that it is invoked with privileges and bypassing its runtime configurability.
A native binary wrapper can make a shell script safe if the wrapper sanitizes the environment. The script must take care not to make too many assumptions (e.g. about the current directory) but this goes. You can use sudo for this provided that it's set up to sanitize the environment. Blacklisting variables is error-prone, so always whitelist. With sudo, make sure that the env_reset
option is turned on, that setenv
is off, and that env_file
and env_keep
only contain innocuous variables.
All these considerations apply equally for any privilege elevation: setuid, setgid, setcap.
Recycled from https://unix.stackexchange.com/questions/364/allow-setuid-on-shell-scripts/2910#2910
add a comment |Â
up vote
2
down vote
Primarily because
Many kernels suffer from a race condition which can allow you to
exchange the shellscript for another executable of your choice between
the times that the newly exec()ed process goes setuid, and when the
command interpreter gets started up. If you are persistent enough, in
theory you could get the kernel to run any program you want.
as well as other reasons found at that link (but the kernel race condition being the most pernicious). Scripts, of course, load differently than binary programs, which is where the fault creeps in.
You might be amused to read this 2001 Dr. Dobb's article - which goes through 6 steps towards writing more secure SUID shell scripts, only to reach step 7:
Lesson Seven -- Don't use SUID shell scripts.
Even after all our work, it is nearly impossible to create safe SUID
shell scripts. (It is impossible on most systems.) Because of these
problems, some systems (e.g., Linux) won't honor SUID on shell
scripts.
This history talks about which variants had fixes for the race condition; the list is larger than you'd think... but setuid scripts are still largely discouraged because of other problems or because it's easier to discourage them than to remember whether you're running on a safe or unsafe variant.
This was a big enough problem, back in the day, that it's instructive to read about how aggressively Perl approached compensating for it.
add a comment |Â
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
There is a race condition inherent to the way shebang (#!
) is typically implemented:
- The kernel opens the executable, and finds that it starts with
#!
. - The kernel closes the executable and opens the interpreter instead.
- The kernel inserts the path to the script to the argument list (as
argv[1]
), and executes the interpreter.
If setuid scripts are allowed with this implementation, an attacker can invoke an arbitrary script by creating a symbolic link to an existing setuid script, executing it, and arranging to change the link after the kernel has performed step 1 and before the interpreter gets around to opening its first argument. For this reason, all modern unices ignore the setuid bit when they detect a shebang.
One way to secure this implementation would be for the kernel to lock the script file until the interpreter has opened it (note that this must prevent not only unlinking or overwriting the file, but also renaming any directory in the path). But unix systems tend to shy away from mandatory locks, and symbolic links would make a correct lock feature especially difficult and invasive. I don't think anyone does it this way.
A few unix systems implement secure setuid shebang using an additional feature: the path /dev/fd/N
refers to the file already opened on file descriptor N (so opening /dev/fd/N
is roughly equivalent to dup(N)
).
- The kernel opens the executable, and finds that it starts with
#!
. Let's say the file descriptor for the executable is 3. - The kernel opens the interpreter.
- The kernel inserts
/dev/fd/3
the argument list (asargv[1]
), and executes the interpreter.
All modern unix variants including Linux implement /dev/fd
, but most do not allow setuid scripts. OpenBSD, NetBSD and MacÃÂ OSÃÂ X support it if you enable a non-default kernel setting. On Linux, people have written patches to allow it but those patches never got merged. Sven Mascheck's shebang page has a lot of information on shebang across unices, including setuid support.
In addition, programs running with elevated privileges have inherent risks that are typically harder to control in higher-level programming languages unless the interpreter was specifically designed for it. The reason is that the programming language runtime's initialization code may perform actions with elevated privileges, based on data that's inherited from the lower-privilege caller, before the program's own code has had the opportunity to sanitize this data. The C runtime does very little for the programmer, so C programs have a better opportunity to take control and sanitize data before anything bad can happen.
Let's assume you've managed to make your program run as root, either because your OS supports setuid shebang or because you've used a native binary wrapper (such as sudo
). Have you opened a security hole? Maybe. The issue here is not about interpreted vs compiled programs. The issue is whether your runtime system behaves safely if executed with privileges.
Any dynamically linked native binary executable is in a way interpreted by the dynamic loader (e.g.
/lib/ld.so
), which loads the dynamic libraries required by the program. On many unices, you can configure the search path for dynamic libraries through the environment (LD_LIBRARY_PATH
is a common name for the environment variable), and even load additional libraries into all executed binaries (LD_PRELOAD
). The invoker of the program can execute arbitrary code in that program's context by placing a specially-craftedlibc.so
in$LD_LIBRARY_PATH
(amongst other tactics). All sane systems ignore theLD_*
variables in setuid executables.In shells such as sh, csh and derivatives, environment variables automatically become shell parameters. Through parameters such as
PATH
,IFS
, and many more, the invoker of the script has many opportunities to execute arbitrary code in the shell scripts's context. Some shells set these variables to sane defaults if they detect that the script has been invoked with privileges, but I don't know that there is any particular implementation that I would trust.Most runtime environments (whether native, bytecode or interpreted) have similar features. Few take special precautions in setuid executables, though the ones that run native code often don't do anything fancier than dynamic linking (which does take precautions).
Perl is a notable exception. It explicitly supports setuid scripts in a secure way. In fact, your script can run setuid even if your OS ignored the setuid bit on scripts. This is because perl ships with a setuid root helper that performs the necessary checks and reinvokes the interpreter on the desired scripts with the desired privileges. This is explained in the perlsec manual. It used to be that setuid perl scripts needed
#!/usr/bin/suidperl -wT
instead of#!/usr/bin/perl -wT
, but on most modern systems,#!/usr/bin/perl -wT
is sufficient.
Note that using a native binary wrapper does nothing in itself to prevent these problems. In fact, it can make the situation worse, because it might prevent your runtime environment from detecting that it is invoked with privileges and bypassing its runtime configurability.
A native binary wrapper can make a shell script safe if the wrapper sanitizes the environment. The script must take care not to make too many assumptions (e.g. about the current directory) but this goes. You can use sudo for this provided that it's set up to sanitize the environment. Blacklisting variables is error-prone, so always whitelist. With sudo, make sure that the env_reset
option is turned on, that setenv
is off, and that env_file
and env_keep
only contain innocuous variables.
All these considerations apply equally for any privilege elevation: setuid, setgid, setcap.
Recycled from https://unix.stackexchange.com/questions/364/allow-setuid-on-shell-scripts/2910#2910
add a comment |Â
up vote
3
down vote
There is a race condition inherent to the way shebang (#!
) is typically implemented:
- The kernel opens the executable, and finds that it starts with
#!
. - The kernel closes the executable and opens the interpreter instead.
- The kernel inserts the path to the script to the argument list (as
argv[1]
), and executes the interpreter.
If setuid scripts are allowed with this implementation, an attacker can invoke an arbitrary script by creating a symbolic link to an existing setuid script, executing it, and arranging to change the link after the kernel has performed step 1 and before the interpreter gets around to opening its first argument. For this reason, all modern unices ignore the setuid bit when they detect a shebang.
One way to secure this implementation would be for the kernel to lock the script file until the interpreter has opened it (note that this must prevent not only unlinking or overwriting the file, but also renaming any directory in the path). But unix systems tend to shy away from mandatory locks, and symbolic links would make a correct lock feature especially difficult and invasive. I don't think anyone does it this way.
A few unix systems implement secure setuid shebang using an additional feature: the path /dev/fd/N
refers to the file already opened on file descriptor N (so opening /dev/fd/N
is roughly equivalent to dup(N)
).
- The kernel opens the executable, and finds that it starts with
#!
. Let's say the file descriptor for the executable is 3. - The kernel opens the interpreter.
- The kernel inserts
/dev/fd/3
the argument list (asargv[1]
), and executes the interpreter.
All modern unix variants including Linux implement /dev/fd
, but most do not allow setuid scripts. OpenBSD, NetBSD and MacÃÂ OSÃÂ X support it if you enable a non-default kernel setting. On Linux, people have written patches to allow it but those patches never got merged. Sven Mascheck's shebang page has a lot of information on shebang across unices, including setuid support.
In addition, programs running with elevated privileges have inherent risks that are typically harder to control in higher-level programming languages unless the interpreter was specifically designed for it. The reason is that the programming language runtime's initialization code may perform actions with elevated privileges, based on data that's inherited from the lower-privilege caller, before the program's own code has had the opportunity to sanitize this data. The C runtime does very little for the programmer, so C programs have a better opportunity to take control and sanitize data before anything bad can happen.
Let's assume you've managed to make your program run as root, either because your OS supports setuid shebang or because you've used a native binary wrapper (such as sudo
). Have you opened a security hole? Maybe. The issue here is not about interpreted vs compiled programs. The issue is whether your runtime system behaves safely if executed with privileges.
Any dynamically linked native binary executable is in a way interpreted by the dynamic loader (e.g.
/lib/ld.so
), which loads the dynamic libraries required by the program. On many unices, you can configure the search path for dynamic libraries through the environment (LD_LIBRARY_PATH
is a common name for the environment variable), and even load additional libraries into all executed binaries (LD_PRELOAD
). The invoker of the program can execute arbitrary code in that program's context by placing a specially-craftedlibc.so
in$LD_LIBRARY_PATH
(amongst other tactics). All sane systems ignore theLD_*
variables in setuid executables.In shells such as sh, csh and derivatives, environment variables automatically become shell parameters. Through parameters such as
PATH
,IFS
, and many more, the invoker of the script has many opportunities to execute arbitrary code in the shell scripts's context. Some shells set these variables to sane defaults if they detect that the script has been invoked with privileges, but I don't know that there is any particular implementation that I would trust.Most runtime environments (whether native, bytecode or interpreted) have similar features. Few take special precautions in setuid executables, though the ones that run native code often don't do anything fancier than dynamic linking (which does take precautions).
Perl is a notable exception. It explicitly supports setuid scripts in a secure way. In fact, your script can run setuid even if your OS ignored the setuid bit on scripts. This is because perl ships with a setuid root helper that performs the necessary checks and reinvokes the interpreter on the desired scripts with the desired privileges. This is explained in the perlsec manual. It used to be that setuid perl scripts needed
#!/usr/bin/suidperl -wT
instead of#!/usr/bin/perl -wT
, but on most modern systems,#!/usr/bin/perl -wT
is sufficient.
Note that using a native binary wrapper does nothing in itself to prevent these problems. In fact, it can make the situation worse, because it might prevent your runtime environment from detecting that it is invoked with privileges and bypassing its runtime configurability.
A native binary wrapper can make a shell script safe if the wrapper sanitizes the environment. The script must take care not to make too many assumptions (e.g. about the current directory) but this goes. You can use sudo for this provided that it's set up to sanitize the environment. Blacklisting variables is error-prone, so always whitelist. With sudo, make sure that the env_reset
option is turned on, that setenv
is off, and that env_file
and env_keep
only contain innocuous variables.
All these considerations apply equally for any privilege elevation: setuid, setgid, setcap.
Recycled from https://unix.stackexchange.com/questions/364/allow-setuid-on-shell-scripts/2910#2910
add a comment |Â
up vote
3
down vote
up vote
3
down vote
There is a race condition inherent to the way shebang (#!
) is typically implemented:
- The kernel opens the executable, and finds that it starts with
#!
. - The kernel closes the executable and opens the interpreter instead.
- The kernel inserts the path to the script to the argument list (as
argv[1]
), and executes the interpreter.
If setuid scripts are allowed with this implementation, an attacker can invoke an arbitrary script by creating a symbolic link to an existing setuid script, executing it, and arranging to change the link after the kernel has performed step 1 and before the interpreter gets around to opening its first argument. For this reason, all modern unices ignore the setuid bit when they detect a shebang.
One way to secure this implementation would be for the kernel to lock the script file until the interpreter has opened it (note that this must prevent not only unlinking or overwriting the file, but also renaming any directory in the path). But unix systems tend to shy away from mandatory locks, and symbolic links would make a correct lock feature especially difficult and invasive. I don't think anyone does it this way.
A few unix systems implement secure setuid shebang using an additional feature: the path /dev/fd/N
refers to the file already opened on file descriptor N (so opening /dev/fd/N
is roughly equivalent to dup(N)
).
- The kernel opens the executable, and finds that it starts with
#!
. Let's say the file descriptor for the executable is 3. - The kernel opens the interpreter.
- The kernel inserts
/dev/fd/3
the argument list (asargv[1]
), and executes the interpreter.
All modern unix variants including Linux implement /dev/fd
, but most do not allow setuid scripts. OpenBSD, NetBSD and MacÃÂ OSÃÂ X support it if you enable a non-default kernel setting. On Linux, people have written patches to allow it but those patches never got merged. Sven Mascheck's shebang page has a lot of information on shebang across unices, including setuid support.
In addition, programs running with elevated privileges have inherent risks that are typically harder to control in higher-level programming languages unless the interpreter was specifically designed for it. The reason is that the programming language runtime's initialization code may perform actions with elevated privileges, based on data that's inherited from the lower-privilege caller, before the program's own code has had the opportunity to sanitize this data. The C runtime does very little for the programmer, so C programs have a better opportunity to take control and sanitize data before anything bad can happen.
Let's assume you've managed to make your program run as root, either because your OS supports setuid shebang or because you've used a native binary wrapper (such as sudo
). Have you opened a security hole? Maybe. The issue here is not about interpreted vs compiled programs. The issue is whether your runtime system behaves safely if executed with privileges.
Any dynamically linked native binary executable is in a way interpreted by the dynamic loader (e.g.
/lib/ld.so
), which loads the dynamic libraries required by the program. On many unices, you can configure the search path for dynamic libraries through the environment (LD_LIBRARY_PATH
is a common name for the environment variable), and even load additional libraries into all executed binaries (LD_PRELOAD
). The invoker of the program can execute arbitrary code in that program's context by placing a specially-craftedlibc.so
in$LD_LIBRARY_PATH
(amongst other tactics). All sane systems ignore theLD_*
variables in setuid executables.In shells such as sh, csh and derivatives, environment variables automatically become shell parameters. Through parameters such as
PATH
,IFS
, and many more, the invoker of the script has many opportunities to execute arbitrary code in the shell scripts's context. Some shells set these variables to sane defaults if they detect that the script has been invoked with privileges, but I don't know that there is any particular implementation that I would trust.Most runtime environments (whether native, bytecode or interpreted) have similar features. Few take special precautions in setuid executables, though the ones that run native code often don't do anything fancier than dynamic linking (which does take precautions).
Perl is a notable exception. It explicitly supports setuid scripts in a secure way. In fact, your script can run setuid even if your OS ignored the setuid bit on scripts. This is because perl ships with a setuid root helper that performs the necessary checks and reinvokes the interpreter on the desired scripts with the desired privileges. This is explained in the perlsec manual. It used to be that setuid perl scripts needed
#!/usr/bin/suidperl -wT
instead of#!/usr/bin/perl -wT
, but on most modern systems,#!/usr/bin/perl -wT
is sufficient.
Note that using a native binary wrapper does nothing in itself to prevent these problems. In fact, it can make the situation worse, because it might prevent your runtime environment from detecting that it is invoked with privileges and bypassing its runtime configurability.
A native binary wrapper can make a shell script safe if the wrapper sanitizes the environment. The script must take care not to make too many assumptions (e.g. about the current directory) but this goes. You can use sudo for this provided that it's set up to sanitize the environment. Blacklisting variables is error-prone, so always whitelist. With sudo, make sure that the env_reset
option is turned on, that setenv
is off, and that env_file
and env_keep
only contain innocuous variables.
All these considerations apply equally for any privilege elevation: setuid, setgid, setcap.
Recycled from https://unix.stackexchange.com/questions/364/allow-setuid-on-shell-scripts/2910#2910
There is a race condition inherent to the way shebang (#!
) is typically implemented:
- The kernel opens the executable, and finds that it starts with
#!
. - The kernel closes the executable and opens the interpreter instead.
- The kernel inserts the path to the script to the argument list (as
argv[1]
), and executes the interpreter.
If setuid scripts are allowed with this implementation, an attacker can invoke an arbitrary script by creating a symbolic link to an existing setuid script, executing it, and arranging to change the link after the kernel has performed step 1 and before the interpreter gets around to opening its first argument. For this reason, all modern unices ignore the setuid bit when they detect a shebang.
One way to secure this implementation would be for the kernel to lock the script file until the interpreter has opened it (note that this must prevent not only unlinking or overwriting the file, but also renaming any directory in the path). But unix systems tend to shy away from mandatory locks, and symbolic links would make a correct lock feature especially difficult and invasive. I don't think anyone does it this way.
A few unix systems implement secure setuid shebang using an additional feature: the path /dev/fd/N
refers to the file already opened on file descriptor N (so opening /dev/fd/N
is roughly equivalent to dup(N)
).
- The kernel opens the executable, and finds that it starts with
#!
. Let's say the file descriptor for the executable is 3. - The kernel opens the interpreter.
- The kernel inserts
/dev/fd/3
the argument list (asargv[1]
), and executes the interpreter.
All modern unix variants including Linux implement /dev/fd
, but most do not allow setuid scripts. OpenBSD, NetBSD and MacÃÂ OSÃÂ X support it if you enable a non-default kernel setting. On Linux, people have written patches to allow it but those patches never got merged. Sven Mascheck's shebang page has a lot of information on shebang across unices, including setuid support.
In addition, programs running with elevated privileges have inherent risks that are typically harder to control in higher-level programming languages unless the interpreter was specifically designed for it. The reason is that the programming language runtime's initialization code may perform actions with elevated privileges, based on data that's inherited from the lower-privilege caller, before the program's own code has had the opportunity to sanitize this data. The C runtime does very little for the programmer, so C programs have a better opportunity to take control and sanitize data before anything bad can happen.
Let's assume you've managed to make your program run as root, either because your OS supports setuid shebang or because you've used a native binary wrapper (such as sudo
). Have you opened a security hole? Maybe. The issue here is not about interpreted vs compiled programs. The issue is whether your runtime system behaves safely if executed with privileges.
Any dynamically linked native binary executable is in a way interpreted by the dynamic loader (e.g.
/lib/ld.so
), which loads the dynamic libraries required by the program. On many unices, you can configure the search path for dynamic libraries through the environment (LD_LIBRARY_PATH
is a common name for the environment variable), and even load additional libraries into all executed binaries (LD_PRELOAD
). The invoker of the program can execute arbitrary code in that program's context by placing a specially-craftedlibc.so
in$LD_LIBRARY_PATH
(amongst other tactics). All sane systems ignore theLD_*
variables in setuid executables.In shells such as sh, csh and derivatives, environment variables automatically become shell parameters. Through parameters such as
PATH
,IFS
, and many more, the invoker of the script has many opportunities to execute arbitrary code in the shell scripts's context. Some shells set these variables to sane defaults if they detect that the script has been invoked with privileges, but I don't know that there is any particular implementation that I would trust.Most runtime environments (whether native, bytecode or interpreted) have similar features. Few take special precautions in setuid executables, though the ones that run native code often don't do anything fancier than dynamic linking (which does take precautions).
Perl is a notable exception. It explicitly supports setuid scripts in a secure way. In fact, your script can run setuid even if your OS ignored the setuid bit on scripts. This is because perl ships with a setuid root helper that performs the necessary checks and reinvokes the interpreter on the desired scripts with the desired privileges. This is explained in the perlsec manual. It used to be that setuid perl scripts needed
#!/usr/bin/suidperl -wT
instead of#!/usr/bin/perl -wT
, but on most modern systems,#!/usr/bin/perl -wT
is sufficient.
Note that using a native binary wrapper does nothing in itself to prevent these problems. In fact, it can make the situation worse, because it might prevent your runtime environment from detecting that it is invoked with privileges and bypassing its runtime configurability.
A native binary wrapper can make a shell script safe if the wrapper sanitizes the environment. The script must take care not to make too many assumptions (e.g. about the current directory) but this goes. You can use sudo for this provided that it's set up to sanitize the environment. Blacklisting variables is error-prone, so always whitelist. With sudo, make sure that the env_reset
option is turned on, that setenv
is off, and that env_file
and env_keep
only contain innocuous variables.
All these considerations apply equally for any privilege elevation: setuid, setgid, setcap.
Recycled from https://unix.stackexchange.com/questions/364/allow-setuid-on-shell-scripts/2910#2910
answered 41 mins ago
Gilles
36.9k1088141
36.9k1088141
add a comment |Â
add a comment |Â
up vote
2
down vote
Primarily because
Many kernels suffer from a race condition which can allow you to
exchange the shellscript for another executable of your choice between
the times that the newly exec()ed process goes setuid, and when the
command interpreter gets started up. If you are persistent enough, in
theory you could get the kernel to run any program you want.
as well as other reasons found at that link (but the kernel race condition being the most pernicious). Scripts, of course, load differently than binary programs, which is where the fault creeps in.
You might be amused to read this 2001 Dr. Dobb's article - which goes through 6 steps towards writing more secure SUID shell scripts, only to reach step 7:
Lesson Seven -- Don't use SUID shell scripts.
Even after all our work, it is nearly impossible to create safe SUID
shell scripts. (It is impossible on most systems.) Because of these
problems, some systems (e.g., Linux) won't honor SUID on shell
scripts.
This history talks about which variants had fixes for the race condition; the list is larger than you'd think... but setuid scripts are still largely discouraged because of other problems or because it's easier to discourage them than to remember whether you're running on a safe or unsafe variant.
This was a big enough problem, back in the day, that it's instructive to read about how aggressively Perl approached compensating for it.
add a comment |Â
up vote
2
down vote
Primarily because
Many kernels suffer from a race condition which can allow you to
exchange the shellscript for another executable of your choice between
the times that the newly exec()ed process goes setuid, and when the
command interpreter gets started up. If you are persistent enough, in
theory you could get the kernel to run any program you want.
as well as other reasons found at that link (but the kernel race condition being the most pernicious). Scripts, of course, load differently than binary programs, which is where the fault creeps in.
You might be amused to read this 2001 Dr. Dobb's article - which goes through 6 steps towards writing more secure SUID shell scripts, only to reach step 7:
Lesson Seven -- Don't use SUID shell scripts.
Even after all our work, it is nearly impossible to create safe SUID
shell scripts. (It is impossible on most systems.) Because of these
problems, some systems (e.g., Linux) won't honor SUID on shell
scripts.
This history talks about which variants had fixes for the race condition; the list is larger than you'd think... but setuid scripts are still largely discouraged because of other problems or because it's easier to discourage them than to remember whether you're running on a safe or unsafe variant.
This was a big enough problem, back in the day, that it's instructive to read about how aggressively Perl approached compensating for it.
add a comment |Â
up vote
2
down vote
up vote
2
down vote
Primarily because
Many kernels suffer from a race condition which can allow you to
exchange the shellscript for another executable of your choice between
the times that the newly exec()ed process goes setuid, and when the
command interpreter gets started up. If you are persistent enough, in
theory you could get the kernel to run any program you want.
as well as other reasons found at that link (but the kernel race condition being the most pernicious). Scripts, of course, load differently than binary programs, which is where the fault creeps in.
You might be amused to read this 2001 Dr. Dobb's article - which goes through 6 steps towards writing more secure SUID shell scripts, only to reach step 7:
Lesson Seven -- Don't use SUID shell scripts.
Even after all our work, it is nearly impossible to create safe SUID
shell scripts. (It is impossible on most systems.) Because of these
problems, some systems (e.g., Linux) won't honor SUID on shell
scripts.
This history talks about which variants had fixes for the race condition; the list is larger than you'd think... but setuid scripts are still largely discouraged because of other problems or because it's easier to discourage them than to remember whether you're running on a safe or unsafe variant.
This was a big enough problem, back in the day, that it's instructive to read about how aggressively Perl approached compensating for it.
Primarily because
Many kernels suffer from a race condition which can allow you to
exchange the shellscript for another executable of your choice between
the times that the newly exec()ed process goes setuid, and when the
command interpreter gets started up. If you are persistent enough, in
theory you could get the kernel to run any program you want.
as well as other reasons found at that link (but the kernel race condition being the most pernicious). Scripts, of course, load differently than binary programs, which is where the fault creeps in.
You might be amused to read this 2001 Dr. Dobb's article - which goes through 6 steps towards writing more secure SUID shell scripts, only to reach step 7:
Lesson Seven -- Don't use SUID shell scripts.
Even after all our work, it is nearly impossible to create safe SUID
shell scripts. (It is impossible on most systems.) Because of these
problems, some systems (e.g., Linux) won't honor SUID on shell
scripts.
This history talks about which variants had fixes for the race condition; the list is larger than you'd think... but setuid scripts are still largely discouraged because of other problems or because it's easier to discourage them than to remember whether you're running on a safe or unsafe variant.
This was a big enough problem, back in the day, that it's instructive to read about how aggressively Perl approached compensating for it.
edited 50 mins ago
answered 1 hour ago
gowenfawr
50.2k10107152
50.2k10107152
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fsecurity.stackexchange.com%2fquestions%2f194166%2fwhy-is-suid-disabled-for-shell-scripts-but-not-for-binaries%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password