Why is SUID disabled for shell scripts but not for binaries?

The name of the pictureThe name of the pictureThe name of the pictureClash 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?










share|improve this question



























    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?










    share|improve this question























      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?










      share|improve this question













      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






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked 2 hours ago









      Cyker

      686510




      686510




















          2 Answers
          2






          active

          oldest

          votes

















          up vote
          3
          down vote













          There is a race condition inherent to the way shebang (#!) is typically implemented:



          1. The kernel opens the executable, and finds that it starts with #!.

          2. The kernel closes the executable and opens the interpreter instead.

          3. 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)).



          1. The kernel opens the executable, and finds that it starts with #!. Let's say the file descriptor for the executable is 3.

          2. The kernel opens the interpreter.

          3. The kernel inserts /dev/fd/3 the argument list (as argv[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-crafted libc.so in $LD_LIBRARY_PATH (amongst other tactics). All sane systems ignore the LD_* 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






          share|improve this answer



























            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.






            share|improve this answer






















              Your Answer







              StackExchange.ready(function()
              var channelOptions =
              tags: "".split(" "),
              id: "162"
              ;
              initTagRenderer("".split(" "), "".split(" "), channelOptions);

              StackExchange.using("externalEditor", function()
              // Have to fire editor after snippets, if snippets enabled
              if (StackExchange.settings.snippets.snippetsEnabled)
              StackExchange.using("snippets", function()
              createEditor();
              );

              else
              createEditor();

              );

              function createEditor()
              StackExchange.prepareEditor(
              heartbeatType: 'answer',
              convertImagesToLinks: false,
              noModals: false,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: null,
              bindNavPrevention: true,
              postfix: "",
              noCode: true, onDemand: true,
              discardSelector: ".discard-answer"
              ,immediatelyShowMarkdownHelp:true
              );



              );













               

              draft saved


              draft discarded


















              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






























              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:



              1. The kernel opens the executable, and finds that it starts with #!.

              2. The kernel closes the executable and opens the interpreter instead.

              3. 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)).



              1. The kernel opens the executable, and finds that it starts with #!. Let's say the file descriptor for the executable is 3.

              2. The kernel opens the interpreter.

              3. The kernel inserts /dev/fd/3 the argument list (as argv[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-crafted libc.so in $LD_LIBRARY_PATH (amongst other tactics). All sane systems ignore the LD_* 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






              share|improve this answer
























                up vote
                3
                down vote













                There is a race condition inherent to the way shebang (#!) is typically implemented:



                1. The kernel opens the executable, and finds that it starts with #!.

                2. The kernel closes the executable and opens the interpreter instead.

                3. 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)).



                1. The kernel opens the executable, and finds that it starts with #!. Let's say the file descriptor for the executable is 3.

                2. The kernel opens the interpreter.

                3. The kernel inserts /dev/fd/3 the argument list (as argv[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-crafted libc.so in $LD_LIBRARY_PATH (amongst other tactics). All sane systems ignore the LD_* 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






                share|improve this answer






















                  up vote
                  3
                  down vote










                  up vote
                  3
                  down vote









                  There is a race condition inherent to the way shebang (#!) is typically implemented:



                  1. The kernel opens the executable, and finds that it starts with #!.

                  2. The kernel closes the executable and opens the interpreter instead.

                  3. 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)).



                  1. The kernel opens the executable, and finds that it starts with #!. Let's say the file descriptor for the executable is 3.

                  2. The kernel opens the interpreter.

                  3. The kernel inserts /dev/fd/3 the argument list (as argv[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-crafted libc.so in $LD_LIBRARY_PATH (amongst other tactics). All sane systems ignore the LD_* 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






                  share|improve this answer












                  There is a race condition inherent to the way shebang (#!) is typically implemented:



                  1. The kernel opens the executable, and finds that it starts with #!.

                  2. The kernel closes the executable and opens the interpreter instead.

                  3. 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)).



                  1. The kernel opens the executable, and finds that it starts with #!. Let's say the file descriptor for the executable is 3.

                  2. The kernel opens the interpreter.

                  3. The kernel inserts /dev/fd/3 the argument list (as argv[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-crafted libc.so in $LD_LIBRARY_PATH (amongst other tactics). All sane systems ignore the LD_* 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







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered 41 mins ago









                  Gilles

                  36.9k1088141




                  36.9k1088141






















                      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.






                      share|improve this answer


























                        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.






                        share|improve this answer
























                          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.






                          share|improve this answer














                          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.







                          share|improve this answer














                          share|improve this answer



                          share|improve this answer








                          edited 50 mins ago

























                          answered 1 hour ago









                          gowenfawr

                          50.2k10107152




                          50.2k10107152



























                               

                              draft saved


                              draft discarded















































                               


                              draft saved


                              draft discarded














                              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













































































                              Comments

                              Popular posts from this blog

                              Long meetings (6-7 hours a day): Being “babysat” by supervisor

                              Is the Concept of Multiple Fantasy Races Scientifically Flawed? [closed]

                              Confectionery