Talking about the Linux environment, why setting the user ID bit is invalid for shell scripts

Foreword:

In a recent project of the blogger, members of the project team needed to delete the cache files created by the daemon daemon. But deleting these cache files requires superuser privileges. The blogger uses the root user to create a shell script that sets the user id to achieve this. But this script is not working as expected. Today, I will talk about setting the user id bit, why it is invalid for shell scripts.

 

Suppose our file structure is as shown below


 There are three cache files in the Cache file directory, namely cache1, cache2, and cache3. These three files are owned by the daemon user.

 

Let's take a look at the two ways in which setting the user id bit works.

 

  • Set "set user id bit" on shell script file

Now we have a shell script called "d_cache.sh", the function of the script is to delete all cache files in the Cache file directory

 

 Running the script, it shows unauthorized operation


 

Now we change to root user and set the set user id bit, and execute the script

 

As shown in the figure, although the owner of the file is changed to root, and the set user id bit is set, it is "unlikely".

 

  • Set "set user id bit" on executable binary

Now we go through the execl function in a C program called system.c, exec the "d_cache.sh" script, and see what happens.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

int main(void)
{
    pid_t pid;
    if ((pid = fork()) < 0) {
        printf("fork error: %s\n", strerror(errno));
        exit(0);
    } else if (pid == 0) { /* child process */
         /* Set the actual user id, effective user id, and saved set user id to 0 (that is, super user) */
        setuid(0);
        if (execl("/home/paul/test/d_cache.sh", "d_cache.sh", (char *)0) < 0) {
            printf("execl error: %s\n", strerror(errno));
            exit(0);
        }
    }
    exit(0);
}

 

 Compile the system.c file to generate the executable binary file a.out, and the running result is as follows

 

Change the owner of a.out to root, and enable the set user id bit, and then run the result as shown below

 

Successfully deleted the cache files in the Cache file directory.

 

Warning: This implementation has huge security implications, any commands in the file d_cache.sh will be executed with root privileges. It is best to change the access authority of the d_cache.sh file to 755, and change the owner of the file to root, that is, ordinary users cannot modify this file.

 

 

Why does this difference occur?

 

This involves three user IDs associated with the process (not discussing group IDs for now)

  • Actual User ID (Login ID)
  • Effective user ID (for checking permissions on files)
  • Save the set user ID (obtained by exec copying the effective user ID)

Taking the above binary executable file a.out as an example, let's analyze the three user IDs in the

 parent process after the changes of the three user IDs in the running program enter the main and before the fork

  • real user ID = paul (login ID)
  • effective user id = root (since the set user id bit is set, exec sets the effective user id to the owner of the file i.e. root)
  • save set userid=root (exex copy effective userid)

In the child process, before calling setuid(0). The child process inherits the attributes of the parent process. At this time, there are three user IDs in the child process.

  • real user id = paul
  • effective user id = root 
  • save set userid=root

After calling setuid(0) in the child process, the three user IDs of the child process

  • real user id = root
  • effective user id = root 
  • save set userid=root

After this, all operations in the child process will have root privileges.

In the child process, the execl function locates the file "/home/paul/test/d_cache.sh", but the file is not a machine executable file generated by the connection editor, so execl thinks the file is a shell script, so Then call /bin/sh with the path "/home/paul/test/d_cache.sh" as input to the shell. When /bin/sh is bash version 2 or later, if the effective user ID is not equal to the actual user ID, bash will set the effective user ID to the actual user ID. Therefore, in the subprocess of the sysyem.c program, if there is no statement setuid(0) before execl, the subsequently executed d_cache.sh script will not have root privileges.


 Due to a huge security hole , Linux ignores the set user ID bit in all interpreter files (first line starting with #!). This is why setting the user ID bit has no effect on shell scripts.

paul@localhost:~/test$ ls -l d_cache.sh
-rwsrwxr-x 1 root root 49 Dec 27 16:09 d_cache.sh
paul@localhost:~/test$ ./d_cache.sh
rm: cannot remove ‘/home/paul/test/Cache/cache1’: Permission denied
rm: cannot remove ‘/home/paul/test/Cache/cache2’: Permission denied
rm: cannot remove ‘/home/paul/test/Cache/cache3’: Permission denied
paul@localhost:~/test$

 

Conclusion: The above is my superficial understanding of why setuid is invalid for shell scripts. Due to the limitation of my level, if I have any mistakes, please criticize and correct me.

 

references:

1.  UNIX Environment Advanced Programming (Second Edition) Chapter 8 Process Control

2. http://unix.stackexchange.com/questions/364/allow-setuid-on-shell-scripts/2910#2910

3. http://www.faqs.org/faqs/unix-faq/faq/part4/section-7.html

4. https://en.wikipedia.org/wiki/Setuid

 

Copyright statement: This article is an original article by the blogger and may not be reproduced without the blogger's permission.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326924199&siteId=291194637